--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 13:55:56 +0200
+Subject: [PATCH] mac80211: add missing queue/hash initialization to
+ 802.3 xmit
+
+Fixes AQL for encap-offloaded tx
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4200,6 +4200,12 @@ static void ieee80211_8023_xmit(struct i
+ if (is_zero_ether_addr(ra))
+ goto out_free;
+
++ if (local->ops->wake_tx_queue) {
++ u16 queue = __ieee80211_select_queue(sdata, sta, skb);
++ skb_set_queue_mapping(skb, queue);
++ skb_get_hash(skb);
++ }
++
+ multicast = is_multicast_ether_addr(ra);
+
+ if (sta)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 13 Aug 2020 15:37:11 +0200
-Subject: [PATCH] mac80211: rework tx encapsulation offload API
-
-The current API (which lets the driver turn on/off per vif directly) has a
-number of limitations:
-- it does not deal with AP_VLAN
-- conditions for enabling (no tkip, no monitor) are only checked at
- add_interface time
-- no way to indicate 4-addr support
-
-In order to address this, store offload flags in struct ieee80211_vif
-(easy to extend for decap offload later). mac80211 initially sets the enable
-flag, but gives the driver a chance to modify it before its settings are
-applied. In addition to the .add_interface op, a .update_vif_offload op is
-introduced, which can be used for runtime changes.
-
-If a driver can't disable encap offload at runtime, or if it has some extra
-limitations, it can simply override the flags within those ops.
-
-Support for encap offload with 4-address mode interfaces can be enabled
-by setting a flag from .add_interface or .update_vif_offload.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/wireless/ath/ath11k/mac.c
-+++ b/drivers/net/wireless/ath/ath11k/mac.c
-@@ -4150,6 +4150,35 @@ static int ath11k_set_he_mu_sounding_mod
- return ret;
- }
-
-+static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif)
-+{
-+ struct ath11k *ar = hw->priv;
-+ struct ath11k_base *ab = ar->ab;
-+ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
-+ u32 param_id, param_value;
-+ int ret;
-+
-+ param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
-+ if (ath11k_frame_mode != ATH11K_HW_TXRX_ETHERNET ||
-+ (vif->type != NL80211_IFTYPE_STATION &&
-+ vif->type != NL80211_IFTYPE_AP))
-+ vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+
-+ if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
-+ param_value = ATH11K_HW_TXRX_ETHERNET;
-+ else
-+ param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
-+
-+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
-+ param_id, param_value);
-+ if (ret) {
-+ ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
-+ arvif->vdev_id, ret);
-+ vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+ }
-+}
-+
- static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
- {
-@@ -4159,7 +4188,6 @@ static int ath11k_mac_op_add_interface(s
- struct vdev_create_params vdev_param = {0};
- struct peer_create_params peer_param;
- u32 param_id, param_value;
-- int hw_encap = 0;
- u16 nss;
- int i;
- int ret;
-@@ -4253,30 +4281,7 @@ static int ath11k_mac_op_add_interface(s
- list_add(&arvif->list, &ar->arvifs);
- spin_unlock_bh(&ar->data_lock);
-
-- param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
-- if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET)
-- switch (vif->type) {
-- case NL80211_IFTYPE_STATION:
-- case NL80211_IFTYPE_AP_VLAN:
-- case NL80211_IFTYPE_AP:
-- hw_encap = 1;
-- break;
-- default:
-- break;
-- }
--
-- if (ieee80211_set_hw_80211_encap(vif, hw_encap))
-- param_value = ATH11K_HW_TXRX_ETHERNET;
-- else
-- param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
--
-- ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
-- param_id, param_value);
-- if (ret) {
-- ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
-- arvif->vdev_id, ret);
-- goto err_vdev_del;
-- }
-+ ath11k_mac_op_update_vif_offload(hw, vif);
-
- nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
- ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
-@@ -5599,6 +5604,7 @@ static const struct ieee80211_ops ath11k
- .reconfig_complete = ath11k_mac_op_reconfig_complete,
- .add_interface = ath11k_mac_op_add_interface,
- .remove_interface = ath11k_mac_op_remove_interface,
-+ .update_vif_offload = ath11k_mac_op_update_vif_offload,
- .config = ath11k_mac_op_config,
- .bss_info_changed = ath11k_mac_op_bss_info_changed,
- .configure_filter = ath11k_mac_op_configure_filter,
-@@ -5852,6 +5858,7 @@ static int __ath11k_mac_register(struct
- ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
- ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
- ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
-+ ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD);
- if (ht_cap & WMI_HT_CAP_ENABLED) {
- ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION);
- ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW);
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1603,6 +1603,21 @@ enum ieee80211_vif_flags {
- IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
- };
-
-+
-+/**
-+ * enum ieee80211_offload_flags - virtual interface offload flags
-+ *
-+ * @IEEE80211_OFFLOAD_ENCAP_ENABLED: tx encapsulation offload is enabled
-+ * The driver supports sending frames passed as 802.3 frames by mac80211.
-+ * It must also support sending 802.11 packets for the same interface.
-+ * @IEEE80211_OFFLOAD_ENCAP_4ADDR: support 4-address mode encapsulation offload
-+ */
-+
-+enum ieee80211_offload_flags {
-+ IEEE80211_OFFLOAD_ENCAP_ENABLED = BIT(0),
-+ IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1),
-+};
-+
- /**
- * struct ieee80211_vif - per-interface data
- *
-@@ -1623,6 +1638,11 @@ enum ieee80211_vif_flags {
- * these need to be set (or cleared) when the interface is added
- * or, if supported by the driver, the interface type is changed
- * at runtime, mac80211 will never touch this field
-+ * @offloaad_flags: hardware offload capabilities/flags for this interface.
-+ * These are initialized by mac80211 before calling .add_interface,
-+ * .change_interface or .update_vif_offload and updated by the driver
-+ * within these ops, based on supported features or runtime change
-+ * restrictions.
- * @hw_queue: hardware queue for each AC
- * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
- * @chanctx_conf: The channel context this interface is assigned to, or %NULL
-@@ -1659,6 +1679,7 @@ struct ieee80211_vif {
- struct ieee80211_chanctx_conf __rcu *chanctx_conf;
-
- u32 driver_flags;
-+ u32 offload_flags;
-
- #ifdef CPTCFG_MAC80211_DEBUGFS
- struct dentry *debugfs_dir;
-@@ -2325,6 +2346,9 @@ struct ieee80211_txq {
- * aggregating MPDUs with the same keyid, allowing mac80211 to keep Tx
- * A-MPDU sessions active while rekeying with Extended Key ID.
- *
-+ * @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation
-+ * offload
-+ *
- * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
- */
- enum ieee80211_hw_flags {
-@@ -2377,6 +2401,7 @@ enum ieee80211_hw_flags {
- IEEE80211_HW_SUPPORTS_MULTI_BSSID,
- IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
- IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
-+ IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
-
- /* keep last, obviously */
- NUM_IEEE80211_HW_FLAGS
-@@ -3811,6 +3836,8 @@ enum ieee80211_reconfig_type {
- * @set_tid_config: Apply TID specific configurations. This callback may sleep.
- * @reset_tid_config: Reset TID specific configuration for the peer.
- * This callback may sleep.
-+ * @update_vif_config: Update virtual interface offload flags
-+ * This callback may sleep.
- */
- struct ieee80211_ops {
- void (*tx)(struct ieee80211_hw *hw,
-@@ -4122,6 +4149,8 @@ struct ieee80211_ops {
- int (*reset_tid_config)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u8 tids);
-+ void (*update_vif_offload)(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif);
- };
-
- /**
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -504,6 +504,7 @@ static int ieee80211_del_key(struct wiph
- struct ieee80211_local *local = sdata->local;
- struct sta_info *sta;
- struct ieee80211_key *key = NULL;
-+ bool recalc_offload = false;
- int ret;
-
- mutex_lock(&local->sta_mtx);
-@@ -528,6 +529,7 @@ static int ieee80211_del_key(struct wiph
- goto out_unlock;
- }
-
-+ recalc_offload = key->conf.cipher == WLAN_CIPHER_SUITE_TKIP;
- ieee80211_key_free(key, sdata->vif.type == NL80211_IFTYPE_STATION);
-
- ret = 0;
-@@ -535,6 +537,9 @@ static int ieee80211_del_key(struct wiph
- mutex_unlock(&local->key_mtx);
- mutex_unlock(&local->sta_mtx);
-
-+ if (recalc_offload)
-+ ieee80211_recalc_offload(local);
-+
- return ret;
- }
-
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -408,6 +408,7 @@ static const char *hw_flag_names[] = {
- FLAG(SUPPORTS_MULTI_BSSID),
- FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
- FLAG(AMPDU_KEYBORDER_SUPPORT),
-+ FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
- #undef FLAG
- };
-
---- a/net/mac80211/driver-ops.h
-+++ b/net/mac80211/driver-ops.h
-@@ -1385,4 +1385,19 @@ static inline int drv_reset_tid_config(s
-
- return ret;
- }
-+
-+static inline void drv_update_vif_offload(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata)
-+{
-+ might_sleep();
-+ check_sdata_in_driver(sdata);
-+
-+ if (!local->ops->update_vif_offload)
-+ return;
-+
-+ trace_drv_update_vif_offload(local, sdata);
-+ local->ops->update_vif_offload(&local->hw, &sdata->vif);
-+ trace_drv_return_void(local);
-+}
-+
- #endif /* __MAC80211_DRIVER_OPS */
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -990,8 +990,6 @@ struct ieee80211_sub_if_data {
- } debugfs;
- #endif
-
-- bool hw_80211_encap;
--
- /* must be last, dynamically sized area in this! */
- struct ieee80211_vif vif;
- };
-@@ -1769,6 +1767,7 @@ void ieee80211_del_virtual_monitor(struc
- bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
- void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
- bool update_bss);
-+void ieee80211_recalc_offload(struct ieee80211_local *local);
-
- static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
- {
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -43,6 +43,7 @@
- */
-
- static void ieee80211_iface_work(struct work_struct *work);
-+static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata);
-
- bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
- {
-@@ -348,6 +349,99 @@ static int ieee80211_check_queues(struct
- return 0;
- }
-
-+static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype)
-+{
-+ switch (iftype) {
-+ case NL80211_IFTYPE_AP:
-+ case NL80211_IFTYPE_P2P_GO:
-+ case NL80211_IFTYPE_P2P_CLIENT:
-+ case NL80211_IFTYPE_STATION:
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdata)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_key *key;
-+ u32 flags;
-+
-+ flags = sdata->vif.offload_flags;
-+
-+ if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) &&
-+ ieee80211_iftype_supports_encap_offload(sdata->vif.type)) {
-+ flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+ mutex_lock(&local->key_mtx);
-+ list_for_each_entry(key, &sdata->key_list, list) {
-+ if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
-+ continue;
-+ if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP ||
-+ !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-+ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+ }
-+ mutex_unlock(&local->key_mtx);
-+
-+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
-+ local->hw.wiphy->frag_threshold != (u32)-1)
-+ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+
-+ if (local->monitors)
-+ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+ } else {
-+ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+ }
-+
-+ if (sdata->vif.offload_flags == flags)
-+ return false;
-+
-+ sdata->vif.offload_flags = flags;
-+ return true;
-+}
-+
-+
-+static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_sub_if_data *vsdata;
-+
-+ if (ieee80211_set_sdata_offload_flags(sdata)) {
-+ drv_update_vif_offload(local, sdata);
-+ ieee80211_set_vif_encap_ops(sdata);
-+ }
-+
-+ list_for_each_entry(vsdata, &local->interfaces, list) {
-+ if (vsdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
-+ vsdata->bss != &sdata->u.ap)
-+ continue;
-+
-+ ieee80211_set_vif_encap_ops(vsdata);
-+ }
-+}
-+
-+void ieee80211_recalc_offload(struct ieee80211_local *local)
-+{
-+ struct ieee80211_sub_if_data *sdata;
-+
-+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD))
-+ return;
-+
-+ mutex_lock(&local->iflist_mtx);
-+
-+ list_for_each_entry(sdata, &local->interfaces, list) {
-+ if (!ieee80211_sdata_running(sdata))
-+ continue;
-+
-+ ieee80211_recalc_sdata_offload(sdata);
-+ }
-+
-+ mutex_unlock(&local->iflist_mtx);
-+}
-+
- void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
- const int offset)
- {
-@@ -587,6 +681,7 @@ int ieee80211_do_open(struct wireless_de
- if (rtnl_dereference(sdata->bss->beacon)) {
- ieee80211_vif_vlan_copy_chanctx(sdata);
- netif_carrier_on(dev);
-+ ieee80211_set_vif_encap_ops(sdata);
- } else {
- netif_carrier_off(dev);
- }
-@@ -616,6 +711,7 @@ int ieee80211_do_open(struct wireless_de
-
- ieee80211_adjust_monitor_flags(sdata, 1);
- ieee80211_configure_filter(local);
-+ ieee80211_recalc_offload(local);
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
-@@ -625,10 +721,13 @@ int ieee80211_do_open(struct wireless_de
- default:
- if (coming_up) {
- ieee80211_del_virtual_monitor(local);
-+ ieee80211_set_sdata_offload_flags(sdata);
-
- res = drv_add_interface(local, sdata);
- if (res)
- goto err_stop;
-+
-+ ieee80211_set_vif_encap_ops(sdata);
- res = ieee80211_check_queues(sdata,
- ieee80211_vif_type_p2p(&sdata->vif));
- if (res)
-@@ -1286,61 +1385,6 @@ static const struct net_device_ops ieee8
-
- };
-
--static void __ieee80211_set_hw_80211_encap(struct ieee80211_sub_if_data *sdata,
-- bool enable)
--{
-- sdata->dev->netdev_ops = enable ? &ieee80211_dataif_8023_ops :
-- &ieee80211_dataif_ops;
-- sdata->hw_80211_encap = enable;
--}
--
--bool ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
--{
-- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-- struct ieee80211_local *local = sdata->local;
-- struct ieee80211_sub_if_data *iter;
-- struct ieee80211_key *key;
--
-- mutex_lock(&local->iflist_mtx);
-- list_for_each_entry(iter, &local->interfaces, list) {
-- struct ieee80211_sub_if_data *disable = NULL;
--
-- if (vif->type == NL80211_IFTYPE_MONITOR) {
-- disable = iter;
-- __ieee80211_set_hw_80211_encap(iter, false);
-- } else if (iter->vif.type == NL80211_IFTYPE_MONITOR) {
-- disable = sdata;
-- enable = false;
-- }
-- if (disable)
-- sdata_dbg(disable,
-- "disable hw 80211 encap due to mon co-exist\n");
-- }
-- mutex_unlock(&local->iflist_mtx);
--
-- if (enable == sdata->hw_80211_encap)
-- return enable;
--
-- if (!sdata->dev)
-- return false;
--
-- if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
-- (local->hw.wiphy->frag_threshold != (u32)-1))
-- enable = false;
--
-- mutex_lock(&sdata->local->key_mtx);
-- list_for_each_entry(key, &sdata->key_list, list) {
-- if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
-- enable = false;
-- }
-- mutex_unlock(&sdata->local->key_mtx);
--
-- __ieee80211_set_hw_80211_encap(sdata, enable);
--
-- return enable;
--}
--EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
--
- static void ieee80211_if_free(struct net_device *dev)
- {
- free_percpu(netdev_tstats(dev));
-@@ -1371,6 +1415,51 @@ static void ieee80211_if_setup_no_queue(
- #endif
- }
-
-+static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_sub_if_data *bss = sdata;
-+ struct ieee80211_key *key;
-+ bool enabled;
-+
-+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
-+ if (!sdata->bss)
-+ return;
-+
-+ bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
-+ }
-+
-+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
-+ !ieee80211_iftype_supports_encap_offload(bss->vif.type))
-+ return;
-+
-+ enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+ if (sdata->wdev.use_4addr &&
-+ !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
-+ enabled = false;
-+
-+ /*
-+ * Encapsulation offload cannot be used with software crypto, and a per-VLAN
-+ * key may have been set
-+ */
-+ if (enabled && sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
-+ mutex_lock(&local->key_mtx);
-+ list_for_each_entry(key, &sdata->key_list, list) {
-+ if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
-+ continue;
-+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-+ enabled = false;
-+ }
-+ mutex_unlock(&local->key_mtx);
-+ }
-+
-+ sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
-+ &ieee80211_dataif_ops;
-+}
-+
- static void ieee80211_iface_work(struct work_struct *work)
- {
- struct ieee80211_sub_if_data *sdata =
-@@ -1553,7 +1642,6 @@ static void ieee80211_setup_sdata(struct
- sdata->vif.bss_conf.txpower = INT_MIN; /* unset */
-
- sdata->noack_map = 0;
-- sdata->hw_80211_encap = false;
-
- /* only monitor/p2p-device differ */
- if (sdata->dev) {
-@@ -1688,6 +1776,7 @@ static int ieee80211_runtime_change_ifty
-
- ieee80211_teardown_sdata(sdata);
-
-+ ieee80211_set_sdata_offload_flags(sdata);
- ret = drv_change_interface(local, sdata, internal_type, p2p);
- if (ret)
- type = ieee80211_vif_type_p2p(&sdata->vif);
-@@ -1700,6 +1789,7 @@ static int ieee80211_runtime_change_ifty
- ieee80211_check_queues(sdata, type);
-
- ieee80211_setup_sdata(sdata, type);
-+ ieee80211_set_vif_encap_ops(sdata);
-
- err = ieee80211_do_open(&sdata->wdev, false);
- WARN(err, "type change: do_open returned %d", err);
---- a/net/mac80211/key.c
-+++ b/net/mac80211/key.c
-@@ -177,13 +177,6 @@ static int ieee80211_key_enable_hw_accel
- }
- }
-
-- /* TKIP countermeasures don't work in encap offload mode */
-- if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP &&
-- sdata->hw_80211_encap) {
-- sdata_dbg(sdata, "TKIP is not allowed in hw 80211 encap mode\n");
-- return -EINVAL;
-- }
--
- ret = drv_set_key(key->local, SET_KEY, sdata,
- sta ? &sta->sta : NULL, &key->conf);
-
-@@ -219,14 +212,6 @@ static int ieee80211_key_enable_hw_accel
- case WLAN_CIPHER_SUITE_CCMP_256:
- case WLAN_CIPHER_SUITE_GCMP:
- case WLAN_CIPHER_SUITE_GCMP_256:
-- /* We cannot do software crypto of data frames with
-- * encapsulation offload enabled. However for 802.11w to
-- * function properly we need cmac/gmac keys.
-- */
-- if (sdata->hw_80211_encap)
-- return -EINVAL;
-- /* Fall through */
--
- case WLAN_CIPHER_SUITE_AES_CMAC:
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
-@@ -824,6 +809,7 @@ int ieee80211_key_link(struct ieee80211_
- */
- bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION;
- int ret = -EOPNOTSUPP;
-+ bool recalc_offload = false;
-
- mutex_lock(&sdata->local->key_mtx);
-
-@@ -864,11 +850,15 @@ int ieee80211_key_link(struct ieee80211_
- key->local = sdata->local;
- key->sdata = sdata;
- key->sta = sta;
-+ recalc_offload = !old_key && key->conf.cipher == WLAN_CIPHER_SUITE_TKIP;
-
- increment_tailroom_need_count(sdata);
-
- ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
-
-+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-+ recalc_offload = true;
-+
- if (!ret) {
- ieee80211_debugfs_key_add(key);
- ieee80211_key_destroy(old_key, delay_tailroom);
-@@ -879,6 +869,9 @@ int ieee80211_key_link(struct ieee80211_
- out:
- mutex_unlock(&sdata->local->key_mtx);
-
-+ if (recalc_offload)
-+ ieee80211_recalc_offload(sdata->local);
-+
- return ret;
- }
-
---- a/net/mac80211/trace.h
-+++ b/net/mac80211/trace.h
-@@ -2733,6 +2733,12 @@ TRACE_EVENT(drv_get_ftm_responder_stats,
- )
- );
-
-+DEFINE_EVENT(local_sdata_addr_evt, drv_update_vif_offload,
-+ TP_PROTO(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata),
-+ TP_ARGS(local, sdata)
-+);
-+
- #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4264,11 +4264,6 @@ netdev_tx_t ieee80211_subif_start_xmit_8
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct sta_info *sta;
-
-- if (WARN_ON(!sdata->hw_80211_encap)) {
-- kfree_skb(skb);
-- return NETDEV_TX_OK;
-- }
--
- if (unlikely(skb->len < ETH_HLEN)) {
- kfree_skb(skb);
- return NETDEV_TX_OK;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 21:11:25 +0200
+Subject: [PATCH] mac80211: check and refresh aggregation session in
+ encap offload tx
+
+Update the last_tx timestamp to avoid tearing down the aggregation session
+early. Fall back to the slow path if the session setup is still running
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4189,6 +4189,8 @@ static void ieee80211_8023_xmit(struct i
+ bool authorized = false;
+ bool multicast;
+ unsigned char *ra = ehdr->h_dest;
++ struct tid_ampdu_tx *tid_tx;
++ u8 tid;
+
+ if (IS_ERR(sta) || (sta && !sta->uploaded))
+ sta = NULL;
+@@ -4226,6 +4228,22 @@ static void ieee80211_8023_xmit(struct i
+
+ memset(info, 0, sizeof(*info));
+
++ if (sta) {
++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++ if (tid_tx) {
++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
++ /* fall back to non-offload slow path */
++ __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
++ return;
++ }
++
++ info->flags |= IEEE80211_TX_CTL_AMPDU;
++ if (tid_tx->timeout)
++ tid_tx->last_tx = jiffies;
++ }
++ }
++
+ if (unlikely(!multicast && skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 13:16:59 +0200
-Subject: [PATCH] mac80211: reduce duplication in tx status functions
-
-Move redundant functionality from __ieee80211_tx_status into
-ieee80211_tx_status_ext. Preparation for unifying with the 802.3 tx status
-codepath.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -184,18 +184,6 @@ static void ieee80211_frame_acked(struct
- struct ieee80211_mgmt *mgmt = (void *) skb->data;
- struct ieee80211_local *local = sta->local;
- struct ieee80211_sub_if_data *sdata = sta->sdata;
-- struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
--
-- if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
-- sta->status_stats.last_ack = jiffies;
-- if (txinfo->status.is_valid_ack_signal) {
-- sta->status_stats.last_ack_signal =
-- (s8)txinfo->status.ack_signal;
-- sta->status_stats.ack_signal_filled = true;
-- ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
-- -txinfo->status.ack_signal);
-- }
-- }
-
- if (ieee80211_is_data_qos(mgmt->frame_control)) {
- struct ieee80211_hdr *hdr = (void *) skb->data;
-@@ -899,7 +887,8 @@ void ieee80211_tx_monitor(struct ieee802
- }
-
- static void __ieee80211_tx_status(struct ieee80211_hw *hw,
-- struct ieee80211_tx_status *status)
-+ struct ieee80211_tx_status *status,
-+ int rates_idx, int retry_count)
- {
- struct sk_buff *skb = status->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-@@ -908,8 +897,6 @@ static void __ieee80211_tx_status(struct
- struct sta_info *sta;
- __le16 fc;
- struct ieee80211_supported_band *sband;
-- int retry_count;
-- int rates_idx;
- bool send_to_cooked;
- bool acked;
- bool noack_success;
-@@ -918,8 +905,6 @@ static void __ieee80211_tx_status(struct
- int tid = IEEE80211_NUM_TIDS;
- u16 tx_time_est;
-
-- rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
--
- sband = local->hw.wiphy->bands[info->band];
- fc = hdr->frame_control;
-
-@@ -996,24 +981,14 @@ static void __ieee80211_tx_status(struct
- if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
- ieee80211_handle_filtered_frame(local, sta, skb);
- return;
-- } else {
-+ } else if (ieee80211_is_data_present(fc)) {
- if (!acked && !noack_success)
-- sta->status_stats.retry_failed++;
-- sta->status_stats.retry_count += retry_count;
--
-- if (ieee80211_is_data_present(fc)) {
-- if (!acked && !noack_success)
-- sta->status_stats.msdu_failed[tid]++;
-+ sta->status_stats.msdu_failed[tid]++;
-
-- sta->status_stats.msdu_retries[tid] +=
-- retry_count;
-- }
-+ sta->status_stats.msdu_retries[tid] +=
-+ retry_count;
- }
-
-- rate_control_tx_status(local, sband, status);
-- if (ieee80211_vif_is_mesh(&sta->sdata->vif))
-- ieee80211s_update_metric(local, sta, status);
--
- if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
- ieee80211_frame_acked(sta, skb);
-
-@@ -1038,20 +1013,6 @@ static void __ieee80211_tx_status(struct
- true);
- ieee80211_info_set_tx_time_est(info, 0);
- }
--
-- if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
-- if (acked) {
-- if (sta->status_stats.lost_packets)
-- sta->status_stats.lost_packets = 0;
--
-- /* Track when last TDLS packet was ACKed */
-- sta->status_stats.last_pkt_time = jiffies;
-- } else if (noack_success) {
-- /* nothing to do here, do not account as lost */
-- } else {
-- ieee80211_lost_packet(sta, info);
-- }
-- }
- }
-
- /* SNMP counters
-@@ -1135,7 +1096,7 @@ void ieee80211_tx_status(struct ieee8021
- if (sta)
- status.sta = &sta->sta;
-
-- __ieee80211_tx_status(hw, &status);
-+ ieee80211_tx_status_ext(hw, &status);
- rcu_read_unlock();
- }
- EXPORT_SYMBOL(ieee80211_tx_status);
-@@ -1148,7 +1109,7 @@ void ieee80211_tx_status_ext(struct ieee
- struct ieee80211_sta *pubsta = status->sta;
- struct ieee80211_supported_band *sband;
- struct sta_info *sta;
-- int retry_count;
-+ int rates_idx, retry_count;
- bool acked, noack_success;
-
- if (pubsta) {
-@@ -1158,13 +1119,7 @@ void ieee80211_tx_status_ext(struct ieee
- sta->tx_stats.last_rate_info = *status->rate;
- }
-
-- if (status->skb)
-- return __ieee80211_tx_status(hw, status);
--
-- if (!status->sta)
-- return;
--
-- ieee80211_tx_get_rates(hw, info, &retry_count);
-+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
-
- sband = hw->wiphy->bands[info->band];
-
-@@ -1176,20 +1131,30 @@ void ieee80211_tx_status_ext(struct ieee
- sta->status_stats.retry_failed++;
- sta->status_stats.retry_count += retry_count;
-
-- if (acked) {
-- sta->status_stats.last_ack = jiffies;
-+ if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
-+ if (acked) {
-+ sta->status_stats.last_ack = jiffies;
-
-- if (sta->status_stats.lost_packets)
-- sta->status_stats.lost_packets = 0;
-+ if (sta->status_stats.lost_packets)
-+ sta->status_stats.lost_packets = 0;
-
-- /* Track when last packet was ACKed */
-- sta->status_stats.last_pkt_time = jiffies;
-- } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-- return;
-- } else if (noack_success) {
-- /* nothing to do here, do not account as lost */
-- } else {
-- ieee80211_lost_packet(sta, info);
-+ /* Track when last packet was ACKed */
-+ sta->status_stats.last_pkt_time = jiffies;
-+
-+ if (info->status.is_valid_ack_signal) {
-+ sta->status_stats.last_ack_signal =
-+ (s8)info->status.ack_signal;
-+ sta->status_stats.ack_signal_filled = true;
-+ ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
-+ -info->status.ack_signal);
-+ }
-+ } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-+ return;
-+ } else if (noack_success) {
-+ /* nothing to do here, do not account as lost */
-+ } else {
-+ ieee80211_lost_packet(sta, info);
-+ }
- }
-
- rate_control_tx_status(local, sband, status);
-@@ -1197,6 +1162,10 @@ void ieee80211_tx_status_ext(struct ieee
- ieee80211s_update_metric(local, sta, status);
- }
-
-+ if (status->skb)
-+ return __ieee80211_tx_status(hw, status, rates_idx,
-+ retry_count);
-+
- if (acked || noack_success) {
- I802_DEBUG_INC(local->dot11TransmittedFrameCount);
- if (!pubsta)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 13:29:12 +0200
-Subject: [PATCH] mac80211: remove tx status call to
- ieee80211_sta_register_airtime
-
-All drivers using airtime fairness are calling ieee80211_sta_register_airtime
-directly
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -997,12 +997,6 @@ static void __ieee80211_tx_status(struct
- ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
- acked, info->status.tx_time);
-
-- if (info->status.tx_time &&
-- wiphy_ext_feature_isset(local->hw.wiphy,
-- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
-- ieee80211_sta_register_airtime(&sta->sta, tid,
-- info->status.tx_time, 0);
--
- if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
- /* Do this here to avoid the expensive lookup of the sta
- * in ieee80211_report_used_skb().
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 21 Aug 2020 05:54:10 +0200
+Subject: [PATCH] mac80211: skip encap offload for tx multicast/control
+ packets
+
+This simplifies the checks in the encap offload tx handler and allows using
+it in cases where software crypto is used for multicast packets, e.g. when
+using an AP_VLAN.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4184,88 +4184,47 @@ static void ieee80211_8023_xmit(struct i
+ struct sk_buff *skb)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+- struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+ struct ieee80211_local *local = sdata->local;
+- bool authorized = false;
+- bool multicast;
+- unsigned char *ra = ehdr->h_dest;
+ struct tid_ampdu_tx *tid_tx;
+ u8 tid;
+
+- if (IS_ERR(sta) || (sta && !sta->uploaded))
+- sta = NULL;
+-
+- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+- (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER)))
+- ra = sdata->u.mgd.bssid;
+-
+- if (is_zero_ether_addr(ra))
+- goto out_free;
+-
+ if (local->ops->wake_tx_queue) {
+ u16 queue = __ieee80211_select_queue(sdata, sta, skb);
+ skb_set_queue_mapping(skb, queue);
+ skb_get_hash(skb);
+ }
+
+- multicast = is_multicast_ether_addr(ra);
+-
+- if (sta)
+- authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+-
+- if (!multicast && !authorized &&
+- (ehdr->h_proto != sdata->control_port_protocol ||
+- !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
+- goto out_free;
+-
+- if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
+- !atomic_read(&sdata->u.ap.num_mcast_sta))
+- goto out_free;
+-
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+ goto out_free;
+
+ memset(info, 0, sizeof(*info));
+
+- if (sta) {
+- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
+- if (tid_tx) {
+- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+- /* fall back to non-offload slow path */
+- __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
+- return;
+- }
+-
+- info->flags |= IEEE80211_TX_CTL_AMPDU;
+- if (tid_tx->timeout)
+- tid_tx->last_tx = jiffies;
++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++ if (tid_tx) {
++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
++ /* fall back to non-offload slow path */
++ __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
++ return;
+ }
++
++ info->flags |= IEEE80211_TX_CTL_AMPDU;
++ if (tid_tx->timeout)
++ tid_tx->last_tx = jiffies;
+ }
+
+- if (unlikely(!multicast && skb->sk &&
++ if (unlikely(skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
+ &info->flags, NULL);
+
+- if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
+- if (sdata->control_port_no_encrypt)
+- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+- info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+- }
+-
+- if (multicast)
+- info->flags |= IEEE80211_TX_CTL_NO_ACK;
+-
+ info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+
+ ieee80211_tx_stats(dev, skb->len);
+
+- if (sta) {
+- sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+- sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
+- }
++ sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
++ sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+@@ -4286,6 +4245,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ struct net_device *dev)
+ {
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+ struct sta_info *sta;
+
+ if (WARN_ON(!sdata->hw_80211_encap)) {
+@@ -4302,6 +4262,10 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
+ kfree_skb(skb);
++ else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
++ !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
++ sdata->control_port_protocol == ehdr->h_proto))
++ ieee80211_subif_start_xmit(skb, dev);
+ else
+ ieee80211_8023_xmit(sdata, dev, sta, skb);
+
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 13:29:56 +0200
-Subject: [PATCH] mac80211: optimize station connection monitor
-
-Calling mod_timer for every rx/tx packet can be quite expensive.
-Instead of constantly updating the timer, we can simply let it run out
-and check the timestamp of the last ACK or rx packet to re-arm it.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -2045,8 +2045,6 @@ void ieee80211_dynamic_ps_timer(struct t
- void ieee80211_send_nullfunc(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- bool powersave);
--void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
-- struct ieee80211_hdr *hdr);
- void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
-
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -2432,23 +2432,6 @@ static void ieee80211_set_disassoc(struc
- sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
- }
-
--void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
-- struct ieee80211_hdr *hdr)
--{
-- /*
-- * We can postpone the mgd.timer whenever receiving unicast frames
-- * from AP because we know that the connection is working both ways
-- * at that time. But multicast frames (and hence also beacons) must
-- * be ignored here, because we need to trigger the timer during
-- * data idle periods for sending the periodic probe request to the
-- * AP we're connected to.
-- */
-- if (is_multicast_ether_addr(hdr->addr1))
-- return;
--
-- ieee80211_sta_reset_conn_monitor(sdata);
--}
--
- static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
- {
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-@@ -2521,21 +2504,13 @@ void ieee80211_sta_tx_notify(struct ieee
- {
- ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
-
-- if (!ieee80211_is_data(hdr->frame_control))
-- return;
--
-- if (ieee80211_is_any_nullfunc(hdr->frame_control) &&
-- sdata->u.mgd.probe_send_count > 0) {
-- if (ack)
-- ieee80211_sta_reset_conn_monitor(sdata);
-- else
-- sdata->u.mgd.nullfunc_failed = true;
-- ieee80211_queue_work(&sdata->local->hw, &sdata->work);
-+ if (!ieee80211_is_any_nullfunc(hdr->frame_control) ||
-+ !sdata->u.mgd.probe_send_count)
- return;
-- }
-
-- if (ack)
-- ieee80211_sta_reset_conn_monitor(sdata);
-+ if (!ack)
-+ sdata->u.mgd.nullfunc_failed = true;
-+ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
- }
-
- static void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata,
-@@ -3600,8 +3575,8 @@ static bool ieee80211_assoc_success(stru
- * Start timer to probe the connection to the AP now.
- * Also start the timer that will detect beacon loss.
- */
-- ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
- ieee80211_sta_reset_beacon_monitor(sdata);
-+ ieee80211_sta_reset_conn_monitor(sdata);
-
- ret = true;
- out:
-@@ -4569,10 +4544,26 @@ static void ieee80211_sta_conn_mon_timer
- from_timer(sdata, t, u.mgd.conn_mon_timer);
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
-+ struct sta_info *sta;
-+ unsigned long timeout;
-
- if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
- return;
-
-+ sta = sta_info_get(sdata, ifmgd->bssid);
-+ if (!sta)
-+ return;
-+
-+ timeout = sta->status_stats.last_ack;
-+ if (time_before(sta->status_stats.last_ack, sta->rx_stats.last_rx))
-+ timeout = sta->rx_stats.last_rx;
-+ timeout += IEEE80211_CONNECTION_IDLE_TIME;
-+
-+ if (time_is_before_jiffies(timeout)) {
-+ mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout));
-+ return;
-+ }
-+
- ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
- }
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -1811,9 +1811,6 @@ ieee80211_rx_h_sta_process(struct ieee80
- sta->rx_stats.last_rate = sta_stats_encode_rate(status);
- }
-
-- if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
-- ieee80211_sta_rx_notify(rx->sdata, hdr);
--
- sta->rx_stats.fragments++;
-
- u64_stats_update_begin(&rx->sta->rx_stats.syncp);
-@@ -4148,7 +4145,6 @@ void ieee80211_check_fast_rx(struct sta_
- fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
- fastrx.expected_ds_bits = 0;
- } else {
-- fastrx.sta_notify = sdata->u.mgd.probe_send_count > 0;
- fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
- fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr3);
- fastrx.expected_ds_bits =
-@@ -4378,11 +4374,6 @@ static bool ieee80211_invoke_fast_rx(str
- pskb_trim(skb, skb->len - fast_rx->icv_len))
- goto drop;
-
-- if (unlikely(fast_rx->sta_notify)) {
-- ieee80211_sta_rx_notify(rx->sdata, hdr);
-- fast_rx->sta_notify = false;
-- }
--
- /* statistics part of ieee80211_rx_h_sta_process() */
- if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
- stats->last_signal = status->signal;
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -336,7 +336,6 @@ struct ieee80211_fast_tx {
- * @expected_ds_bits: from/to DS bits expected
- * @icv_len: length of the MIC if present
- * @key: bool indicating encryption is expected (key is set)
-- * @sta_notify: notify the MLME code (once)
- * @internal_forward: forward froms internally on AP/VLAN type interfaces
- * @uses_rss: copy of USES_RSS hw flag
- * @da_offs: offset of the DA in the header (for header conversion)
-@@ -352,7 +351,6 @@ struct ieee80211_fast_rx {
- __le16 expected_ds_bits;
- u8 icv_len;
- u8 key:1,
-- sta_notify:1,
- internal_forward:1,
- uses_rss:1;
- u8 da_offs, sa_offs;
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -1227,9 +1227,6 @@ void ieee80211_tx_status_8023(struct iee
- sta->status_stats.retry_count += retry_count;
-
- if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
-- if (acked && vif->type == NL80211_IFTYPE_STATION)
-- ieee80211_sta_reset_conn_monitor(sdata);
--
- sta->status_stats.last_ack = jiffies;
- if (info->flags & IEEE80211_TX_STAT_ACK) {
- if (sta->status_stats.lost_packets)
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 21 Aug 2020 06:03:45 +0200
+Subject: [PATCH] mac80211: set info->control.hw_key for encap offload
+ packets
+
+This is needed for drivers that don't do the key lookup themselves
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4185,6 +4185,7 @@ static void ieee80211_8023_xmit(struct i
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_key *key;
+ struct tid_ampdu_tx *tid_tx;
+ u8 tid;
+
+@@ -4233,6 +4234,10 @@ static void ieee80211_8023_xmit(struct i
+ info->control.flags |= IEEE80211_TX_CTRL_HW_80211_ENCAP;
+ info->control.vif = &sdata->vif;
+
++ key = rcu_dereference(sta->ptk[sta->ptk_idx]);
++ if (key)
++ info->control.hw_key = &key->conf;
++
+ ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
+
+ return;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 13 Aug 2020 15:37:11 +0200
+Subject: [PATCH] mac80211: rework tx encapsulation offload API
+
+The current API (which lets the driver turn on/off per vif directly) has a
+number of limitations:
+- it does not deal with AP_VLAN
+- conditions for enabling (no tkip, no monitor) are only checked at
+ add_interface time
+- no way to indicate 4-addr support
+
+In order to address this, store offload flags in struct ieee80211_vif
+(easy to extend for decap offload later). mac80211 initially sets the enable
+flag, but gives the driver a chance to modify it before its settings are
+applied. In addition to the .add_interface op, a .update_vif_offload op is
+introduced, which can be used for runtime changes.
+
+If a driver can't disable encap offload at runtime, or if it has some extra
+limitations, it can simply override the flags within those ops.
+
+Support for encap offload with 4-address mode interfaces can be enabled
+by setting a flag from .add_interface or .update_vif_offload.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -4150,6 +4150,35 @@ static int ath11k_set_he_mu_sounding_mod
+ return ret;
+ }
+
++static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif)
++{
++ struct ath11k *ar = hw->priv;
++ struct ath11k_base *ab = ar->ab;
++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
++ u32 param_id, param_value;
++ int ret;
++
++ param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
++ if (ath11k_frame_mode != ATH11K_HW_TXRX_ETHERNET ||
++ (vif->type != NL80211_IFTYPE_STATION &&
++ vif->type != NL80211_IFTYPE_AP))
++ vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++
++ if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
++ param_value = ATH11K_HW_TXRX_ETHERNET;
++ else
++ param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
++
++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
++ param_id, param_value);
++ if (ret) {
++ ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
++ arvif->vdev_id, ret);
++ vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++ }
++}
++
+ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+ {
+@@ -4159,7 +4188,6 @@ static int ath11k_mac_op_add_interface(s
+ struct vdev_create_params vdev_param = {0};
+ struct peer_create_params peer_param;
+ u32 param_id, param_value;
+- int hw_encap = 0;
+ u16 nss;
+ int i;
+ int ret;
+@@ -4253,30 +4281,7 @@ static int ath11k_mac_op_add_interface(s
+ list_add(&arvif->list, &ar->arvifs);
+ spin_unlock_bh(&ar->data_lock);
+
+- param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
+- if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET)
+- switch (vif->type) {
+- case NL80211_IFTYPE_STATION:
+- case NL80211_IFTYPE_AP_VLAN:
+- case NL80211_IFTYPE_AP:
+- hw_encap = 1;
+- break;
+- default:
+- break;
+- }
+-
+- if (ieee80211_set_hw_80211_encap(vif, hw_encap))
+- param_value = ATH11K_HW_TXRX_ETHERNET;
+- else
+- param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
+-
+- ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+- param_id, param_value);
+- if (ret) {
+- ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
+- arvif->vdev_id, ret);
+- goto err_vdev_del;
+- }
++ ath11k_mac_op_update_vif_offload(hw, vif);
+
+ nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+@@ -5599,6 +5604,7 @@ static const struct ieee80211_ops ath11k
+ .reconfig_complete = ath11k_mac_op_reconfig_complete,
+ .add_interface = ath11k_mac_op_add_interface,
+ .remove_interface = ath11k_mac_op_remove_interface,
++ .update_vif_offload = ath11k_mac_op_update_vif_offload,
+ .config = ath11k_mac_op_config,
+ .bss_info_changed = ath11k_mac_op_bss_info_changed,
+ .configure_filter = ath11k_mac_op_configure_filter,
+@@ -5852,6 +5858,7 @@ static int __ath11k_mac_register(struct
+ ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
+ ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
+ ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
++ ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ if (ht_cap & WMI_HT_CAP_ENABLED) {
+ ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW);
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1603,6 +1603,21 @@ enum ieee80211_vif_flags {
+ IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
+ };
+
++
++/**
++ * enum ieee80211_offload_flags - virtual interface offload flags
++ *
++ * @IEEE80211_OFFLOAD_ENCAP_ENABLED: tx encapsulation offload is enabled
++ * The driver supports sending frames passed as 802.3 frames by mac80211.
++ * It must also support sending 802.11 packets for the same interface.
++ * @IEEE80211_OFFLOAD_ENCAP_4ADDR: support 4-address mode encapsulation offload
++ */
++
++enum ieee80211_offload_flags {
++ IEEE80211_OFFLOAD_ENCAP_ENABLED = BIT(0),
++ IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1),
++};
++
+ /**
+ * struct ieee80211_vif - per-interface data
+ *
+@@ -1623,6 +1638,11 @@ enum ieee80211_vif_flags {
+ * these need to be set (or cleared) when the interface is added
+ * or, if supported by the driver, the interface type is changed
+ * at runtime, mac80211 will never touch this field
++ * @offloaad_flags: hardware offload capabilities/flags for this interface.
++ * These are initialized by mac80211 before calling .add_interface,
++ * .change_interface or .update_vif_offload and updated by the driver
++ * within these ops, based on supported features or runtime change
++ * restrictions.
+ * @hw_queue: hardware queue for each AC
+ * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
+ * @chanctx_conf: The channel context this interface is assigned to, or %NULL
+@@ -1659,6 +1679,7 @@ struct ieee80211_vif {
+ struct ieee80211_chanctx_conf __rcu *chanctx_conf;
+
+ u32 driver_flags;
++ u32 offload_flags;
+
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+ struct dentry *debugfs_dir;
+@@ -2325,6 +2346,9 @@ struct ieee80211_txq {
+ * aggregating MPDUs with the same keyid, allowing mac80211 to keep Tx
+ * A-MPDU sessions active while rekeying with Extended Key ID.
+ *
++ * @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation
++ * offload
++ *
+ * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
+ */
+ enum ieee80211_hw_flags {
+@@ -2377,6 +2401,7 @@ enum ieee80211_hw_flags {
+ IEEE80211_HW_SUPPORTS_MULTI_BSSID,
+ IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
+ IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
++ IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
+
+ /* keep last, obviously */
+ NUM_IEEE80211_HW_FLAGS
+@@ -3811,6 +3836,8 @@ enum ieee80211_reconfig_type {
+ * @set_tid_config: Apply TID specific configurations. This callback may sleep.
+ * @reset_tid_config: Reset TID specific configuration for the peer.
+ * This callback may sleep.
++ * @update_vif_config: Update virtual interface offload flags
++ * This callback may sleep.
+ */
+ struct ieee80211_ops {
+ void (*tx)(struct ieee80211_hw *hw,
+@@ -4122,6 +4149,8 @@ struct ieee80211_ops {
+ int (*reset_tid_config)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 tids);
++ void (*update_vif_offload)(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif);
+ };
+
+ /**
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -408,6 +408,7 @@ static const char *hw_flag_names[] = {
+ FLAG(SUPPORTS_MULTI_BSSID),
+ FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
+ FLAG(AMPDU_KEYBORDER_SUPPORT),
++ FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
+ #undef FLAG
+ };
+
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1385,4 +1385,19 @@ static inline int drv_reset_tid_config(s
+
+ return ret;
+ }
++
++static inline void drv_update_vif_offload(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata)
++{
++ might_sleep();
++ check_sdata_in_driver(sdata);
++
++ if (!local->ops->update_vif_offload)
++ return;
++
++ trace_drv_update_vif_offload(local, sdata);
++ local->ops->update_vif_offload(&local->hw, &sdata->vif);
++ trace_drv_return_void(local);
++}
++
+ #endif /* __MAC80211_DRIVER_OPS */
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -990,8 +990,6 @@ struct ieee80211_sub_if_data {
+ } debugfs;
+ #endif
+
+- bool hw_80211_encap;
+-
+ /* must be last, dynamically sized area in this! */
+ struct ieee80211_vif vif;
+ };
+@@ -1769,6 +1767,7 @@ void ieee80211_del_virtual_monitor(struc
+ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+ bool update_bss);
++void ieee80211_recalc_offload(struct ieee80211_local *local);
+
+ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
+ {
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -43,6 +43,7 @@
+ */
+
+ static void ieee80211_iface_work(struct work_struct *work);
++static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata);
+
+ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ {
+@@ -348,6 +349,85 @@ static int ieee80211_check_queues(struct
+ return 0;
+ }
+
++static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype)
++{
++ switch (iftype) {
++ /* P2P GO and client are mapped to AP/STATION types */
++ case NL80211_IFTYPE_AP:
++ case NL80211_IFTYPE_STATION:
++ return true;
++ default:
++ return false;
++ }
++}
++
++static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdata)
++{
++ struct ieee80211_local *local = sdata->local;
++ u32 flags;
++
++ flags = sdata->vif.offload_flags;
++
++ if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) &&
++ ieee80211_iftype_supports_encap_offload(sdata->vif.type)) {
++ flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED;
++
++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
++ local->hw.wiphy->frag_threshold != (u32)-1)
++ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++
++ if (local->monitors)
++ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++ } else {
++ flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++ }
++
++ if (sdata->vif.offload_flags == flags)
++ return false;
++
++ sdata->vif.offload_flags = flags;
++ return true;
++}
++
++
++static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_sub_if_data *vsdata;
++
++ if (ieee80211_set_sdata_offload_flags(sdata)) {
++ drv_update_vif_offload(local, sdata);
++ ieee80211_set_vif_encap_ops(sdata);
++ }
++
++ list_for_each_entry(vsdata, &local->interfaces, list) {
++ if (vsdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
++ vsdata->bss != &sdata->u.ap)
++ continue;
++
++ ieee80211_set_vif_encap_ops(vsdata);
++ }
++}
++
++void ieee80211_recalc_offload(struct ieee80211_local *local)
++{
++ struct ieee80211_sub_if_data *sdata;
++
++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD))
++ return;
++
++ mutex_lock(&local->iflist_mtx);
++
++ list_for_each_entry(sdata, &local->interfaces, list) {
++ if (!ieee80211_sdata_running(sdata))
++ continue;
++
++ ieee80211_recalc_sdata_offload(sdata);
++ }
++
++ mutex_unlock(&local->iflist_mtx);
++}
++
+ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
+ const int offset)
+ {
+@@ -587,6 +667,7 @@ int ieee80211_do_open(struct wireless_de
+ if (rtnl_dereference(sdata->bss->beacon)) {
+ ieee80211_vif_vlan_copy_chanctx(sdata);
+ netif_carrier_on(dev);
++ ieee80211_set_vif_encap_ops(sdata);
+ } else {
+ netif_carrier_off(dev);
+ }
+@@ -616,6 +697,7 @@ int ieee80211_do_open(struct wireless_de
+
+ ieee80211_adjust_monitor_flags(sdata, 1);
+ ieee80211_configure_filter(local);
++ ieee80211_recalc_offload(local);
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
+@@ -625,10 +707,13 @@ int ieee80211_do_open(struct wireless_de
+ default:
+ if (coming_up) {
+ ieee80211_del_virtual_monitor(local);
++ ieee80211_set_sdata_offload_flags(sdata);
+
+ res = drv_add_interface(local, sdata);
+ if (res)
+ goto err_stop;
++
++ ieee80211_set_vif_encap_ops(sdata);
+ res = ieee80211_check_queues(sdata,
+ ieee80211_vif_type_p2p(&sdata->vif));
+ if (res)
+@@ -1286,61 +1371,6 @@ static const struct net_device_ops ieee8
+
+ };
+
+-static void __ieee80211_set_hw_80211_encap(struct ieee80211_sub_if_data *sdata,
+- bool enable)
+-{
+- sdata->dev->netdev_ops = enable ? &ieee80211_dataif_8023_ops :
+- &ieee80211_dataif_ops;
+- sdata->hw_80211_encap = enable;
+-}
+-
+-bool ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
+-{
+- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+- struct ieee80211_local *local = sdata->local;
+- struct ieee80211_sub_if_data *iter;
+- struct ieee80211_key *key;
+-
+- mutex_lock(&local->iflist_mtx);
+- list_for_each_entry(iter, &local->interfaces, list) {
+- struct ieee80211_sub_if_data *disable = NULL;
+-
+- if (vif->type == NL80211_IFTYPE_MONITOR) {
+- disable = iter;
+- __ieee80211_set_hw_80211_encap(iter, false);
+- } else if (iter->vif.type == NL80211_IFTYPE_MONITOR) {
+- disable = sdata;
+- enable = false;
+- }
+- if (disable)
+- sdata_dbg(disable,
+- "disable hw 80211 encap due to mon co-exist\n");
+- }
+- mutex_unlock(&local->iflist_mtx);
+-
+- if (enable == sdata->hw_80211_encap)
+- return enable;
+-
+- if (!sdata->dev)
+- return false;
+-
+- if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
+- (local->hw.wiphy->frag_threshold != (u32)-1))
+- enable = false;
+-
+- mutex_lock(&sdata->local->key_mtx);
+- list_for_each_entry(key, &sdata->key_list, list) {
+- if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
+- enable = false;
+- }
+- mutex_unlock(&sdata->local->key_mtx);
+-
+- __ieee80211_set_hw_80211_encap(sdata, enable);
+-
+- return enable;
+-}
+-EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
+-
+ static void ieee80211_if_free(struct net_device *dev)
+ {
+ free_percpu(netdev_tstats(dev));
+@@ -1371,6 +1401,32 @@ static void ieee80211_if_setup_no_queue(
+ #endif
+ }
+
++static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_sub_if_data *bss = sdata;
++ bool enabled;
++
++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++ if (!sdata->bss)
++ return;
++
++ bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++ }
++
++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
++ !ieee80211_iftype_supports_encap_offload(bss->vif.type))
++ return;
++
++ enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
++ if (sdata->wdev.use_4addr &&
++ !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
++ enabled = false;
++
++ sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
++ &ieee80211_dataif_ops;
++}
++
+ static void ieee80211_iface_work(struct work_struct *work)
+ {
+ struct ieee80211_sub_if_data *sdata =
+@@ -1553,7 +1609,6 @@ static void ieee80211_setup_sdata(struct
+ sdata->vif.bss_conf.txpower = INT_MIN; /* unset */
+
+ sdata->noack_map = 0;
+- sdata->hw_80211_encap = false;
+
+ /* only monitor/p2p-device differ */
+ if (sdata->dev) {
+@@ -1688,6 +1743,7 @@ static int ieee80211_runtime_change_ifty
+
+ ieee80211_teardown_sdata(sdata);
+
++ ieee80211_set_sdata_offload_flags(sdata);
+ ret = drv_change_interface(local, sdata, internal_type, p2p);
+ if (ret)
+ type = ieee80211_vif_type_p2p(&sdata->vif);
+@@ -1700,6 +1756,7 @@ static int ieee80211_runtime_change_ifty
+ ieee80211_check_queues(sdata, type);
+
+ ieee80211_setup_sdata(sdata, type);
++ ieee80211_set_vif_encap_ops(sdata);
+
+ err = ieee80211_do_open(&sdata->wdev, false);
+ WARN(err, "type change: do_open returned %d", err);
+--- a/net/mac80211/key.c
++++ b/net/mac80211/key.c
+@@ -177,13 +177,6 @@ static int ieee80211_key_enable_hw_accel
+ }
+ }
+
+- /* TKIP countermeasures don't work in encap offload mode */
+- if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP &&
+- sdata->hw_80211_encap) {
+- sdata_dbg(sdata, "TKIP is not allowed in hw 80211 encap mode\n");
+- return -EINVAL;
+- }
+-
+ ret = drv_set_key(key->local, SET_KEY, sdata,
+ sta ? &sta->sta : NULL, &key->conf);
+
+@@ -219,14 +212,6 @@ static int ieee80211_key_enable_hw_accel
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+- /* We cannot do software crypto of data frames with
+- * encapsulation offload enabled. However for 802.11w to
+- * function properly we need cmac/gmac keys.
+- */
+- if (sdata->hw_80211_encap)
+- return -EINVAL;
+- /* Fall through */
+-
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -2733,6 +2733,12 @@ TRACE_EVENT(drv_get_ftm_responder_stats,
+ )
+ );
+
++DEFINE_EVENT(local_sdata_addr_evt, drv_update_vif_offload,
++ TP_PROTO(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata),
++ TP_ARGS(local, sdata)
++);
++
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+
+ #undef TRACE_INCLUDE_PATH
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4181,11 +4181,10 @@ static bool ieee80211_tx_8023(struct iee
+
+ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ struct net_device *dev, struct sta_info *sta,
+- struct sk_buff *skb)
++ struct ieee80211_key *key, struct sk_buff *skb)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_local *local = sdata->local;
+- struct ieee80211_key *key;
+ struct tid_ampdu_tx *tid_tx;
+ u8 tid;
+
+@@ -4234,7 +4233,6 @@ static void ieee80211_8023_xmit(struct i
+ info->control.flags |= IEEE80211_TX_CTRL_HW_80211_ENCAP;
+ info->control.vif = &sdata->vif;
+
+- key = rcu_dereference(sta->ptk[sta->ptk_idx]);
+ if (key)
+ info->control.hw_key = &key->conf;
+
+@@ -4251,12 +4249,9 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ {
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ethhdr *ehdr = (struct ethhdr *)skb->data;
++ struct ieee80211_key *key;
+ struct sta_info *sta;
+-
+- if (WARN_ON(!sdata->hw_80211_encap)) {
+- kfree_skb(skb);
+- return NETDEV_TX_OK;
+- }
++ bool offload = true;
+
+ if (unlikely(skb->len < ETH_HLEN)) {
+ kfree_skb(skb);
+@@ -4265,15 +4260,26 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+
+ rcu_read_lock();
+
+- if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
++ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ kfree_skb(skb);
+- else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
+- !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
+- sdata->control_port_protocol == ehdr->h_proto))
+- ieee80211_subif_start_xmit(skb, dev);
++ goto out;
++ }
++
++ if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
++ !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
++ sdata->control_port_protocol == ehdr->h_proto))
++ offload = false;
++ else if ((key = rcu_dereference(sta->ptk[sta->ptk_idx])) &&
++ (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) ||
++ key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
++ offload = false;
++
++ if (offload)
++ ieee80211_8023_xmit(sdata, dev, sta, key, skb);
+ else
+- ieee80211_8023_xmit(sdata, dev, sta, skb);
++ ieee80211_subif_start_xmit(skb, dev);
+
++out:
+ rcu_read_unlock();
+
+ return NETDEV_TX_OK;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 13:35:32 +0200
-Subject: [PATCH] mac80211: swap NEED_TXPROCESSING and HW_80211_ENCAP tx
- flags
-
-In order to unify the tx status path, the hw 802.11 encapsulation flag
-needs to survive the trip to the tx status call.
-Since we don't have any free bits in info->flags, we need to move one.
-IEEE80211_TX_INTFL_NEED_TXPROCESSING is only used internally in mac80211,
-and only before the call into the driver.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/wireless/ath/ath11k/dp_tx.c
-+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
-@@ -14,7 +14,7 @@ ath11k_dp_tx_get_encap_type(struct ath11
- {
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-
-- if (tx_info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
-+ if (tx_info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
- return HAL_TCL_ENCAP_TYPE_ETHERNET;
-
- return HAL_TCL_ENCAP_TYPE_NATIVE_WIFI;
-@@ -93,7 +93,7 @@ int ath11k_dp_tx(struct ath11k *ar, stru
- if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
- return -ESHUTDOWN;
-
-- if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) &&
-+ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
- !ieee80211_is_data(hdr->frame_control))
- return -ENOTSUPP;
-
---- a/drivers/net/wireless/ath/ath11k/mac.c
-+++ b/drivers/net/wireless/ath/ath11k/mac.c
-@@ -3749,7 +3749,7 @@ static int ath11k_mac_mgmt_tx_wmi(struct
- return -ENOSPC;
-
- info = IEEE80211_SKB_CB(skb);
-- if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)) {
-+ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
- if ((ieee80211_is_action(hdr->frame_control) ||
- ieee80211_is_deauth(hdr->frame_control) ||
- ieee80211_is_disassoc(hdr->frame_control)) &&
-@@ -3876,7 +3876,7 @@ static void ath11k_mac_op_tx(struct ieee
- bool is_prb_rsp;
- int ret;
-
-- if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) {
-+ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
- skb_cb->flags |= ATH11K_SKB_HW_80211_ENCAP;
- } else if (ieee80211_is_mgmt(hdr->frame_control)) {
- is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -720,9 +720,8 @@ struct ieee80211_bss_conf {
- * @IEEE80211_TX_INTFL_OFFCHAN_TX_OK: Internal to mac80211. Used to indicate
- * that a frame can be transmitted while the queues are stopped for
- * off-channel operation.
-- * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
-- * used to indicate that a pending frame requires TX processing before
-- * it can be sent out.
-+ * @IEEE80211_TX_CTL_HW_80211_ENCAP: This frame uses hardware encapsulation
-+ * (header conversion)
- * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211,
- * used to indicate that a frame was already retried due to PS
- * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211,
-@@ -791,7 +790,7 @@ enum mac80211_tx_info_flags {
- IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11),
- IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12),
- IEEE80211_TX_INTFL_OFFCHAN_TX_OK = BIT(13),
-- IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14),
-+ IEEE80211_TX_CTL_HW_80211_ENCAP = BIT(14),
- IEEE80211_TX_INTFL_RETRIED = BIT(15),
- IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16),
- IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17),
-@@ -823,8 +822,9 @@ enum mac80211_tx_info_flags {
- * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
- * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
- * @IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP: This frame skips mesh path lookup
-- * @IEEE80211_TX_CTRL_HW_80211_ENCAP: This frame uses hardware encapsulation
-- * (header conversion)
-+ * @IEEE80211_TX_INTCFL_NEED_TXPROCESSING: completely internal to mac80211,
-+ * used to indicate that a pending frame requires TX processing before
-+ * it can be sent out.
- *
- * These flags are used in tx_info->control.flags.
- */
-@@ -835,7 +835,7 @@ enum mac80211_tx_control_flags {
- IEEE80211_TX_CTRL_AMSDU = BIT(3),
- IEEE80211_TX_CTRL_FAST_XMIT = BIT(4),
- IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP = BIT(5),
-- IEEE80211_TX_CTRL_HW_80211_ENCAP = BIT(6),
-+ IEEE80211_TX_INTCFL_NEED_TXPROCESSING = BIT(6),
- };
-
- /*
---- a/net/mac80211/mesh_hwmp.c
-+++ b/net/mac80211/mesh_hwmp.c
-@@ -212,7 +212,7 @@ static void prepare_frame_for_deferred_t
- skb->priority = 7;
-
- info->control.vif = &sdata->vif;
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
- ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
- }
-@@ -1163,7 +1163,7 @@ int mesh_nexthop_resolve(struct ieee8021
- if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN)
- skb_to_free = skb_dequeue(&mpath->frame_queue);
-
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
- ieee80211_set_qos_hdr(sdata, skb);
- skb_queue_tail(&mpath->frame_queue, skb);
- if (skb_to_free)
---- a/net/mac80211/mesh_ps.c
-+++ b/net/mac80211/mesh_ps.c
-@@ -432,7 +432,7 @@ static void mpsp_qos_null_append(struct
-
- info = IEEE80211_SKB_CB(new_skb);
- info->control.vif = &sdata->vif;
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
-
- __skb_queue_tail(frames, new_skb);
- }
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2896,7 +2896,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
- fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
- info = IEEE80211_SKB_CB(fwd_skb);
- memset(info, 0, sizeof(*info));
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
- info->control.vif = &rx->sdata->vif;
- info->control.jiffies = jiffies;
- if (is_multicast_ether_addr(fwd_hdr->addr1)) {
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -66,8 +66,8 @@ static void ieee80211_handle_filtered_fr
-
- info->control.jiffies = jiffies;
- info->control.vif = &sta->sdata->vif;
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING |
-- IEEE80211_TX_INTFL_RETRANSMISSION;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
-+ info->flags |= IEEE80211_TX_INTFL_RETRANSMISSION;
- info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
-
- sta->status_stats.filtered++;
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -530,7 +530,7 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
-
- info->control.jiffies = jiffies;
- info->control.vif = &tx->sdata->vif;
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
- info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
- skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
- spin_unlock(&sta->ps_lock);
-@@ -1132,7 +1132,7 @@ static bool ieee80211_tx_prep_agg(struct
- tx->sta->sta.addr, tx->sta->sta.aid);
- }
- info->control.vif = &tx->sdata->vif;
-- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
- info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
- __skb_queue_tail(&tid_tx->pending, skb);
- if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)
-@@ -1177,7 +1177,7 @@ ieee80211_tx_prepare(struct ieee80211_su
- * we are doing the needed processing, so remove the flag
- * now.
- */
-- info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-+ info->control.flags &= ~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
-
- hdr = (struct ieee80211_hdr *) skb->data;
-
-@@ -1256,7 +1256,7 @@ static struct txq_info *ieee80211_get_tx
- (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
- return NULL;
-
-- if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) &&
-+ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
- unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
- if ((!ieee80211_is_mgmt(hdr->frame_control) ||
- ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
-@@ -3640,7 +3640,7 @@ begin:
- else
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-
-- if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
-+ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
- goto encap_out;
-
- if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
-@@ -4247,7 +4247,7 @@ static void ieee80211_8023_xmit(struct i
- sdata = container_of(sdata->bss,
- struct ieee80211_sub_if_data, u.ap);
-
-- info->control.flags |= IEEE80211_TX_CTRL_HW_80211_ENCAP;
-+ info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
- info->control.vif = &sdata->vif;
-
- ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
-@@ -4351,7 +4351,7 @@ static bool ieee80211_tx_pending_skb(str
-
- sdata = vif_to_sdata(info->control.vif);
-
-- if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
-+ if (info->control.flags & IEEE80211_TX_INTCFL_NEED_TXPROCESSING) {
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (unlikely(!chanctx_conf)) {
- dev_kfree_skb(skb);
-@@ -4359,7 +4359,7 @@ static bool ieee80211_tx_pending_skb(str
- }
- info->band = chanctx_conf->def.chan->band;
- result = ieee80211_tx(sdata, NULL, skb, true, 0);
-- } else if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) {
-+ } else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
- if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
- dev_kfree_skb(skb);
- return true;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 13:16:59 +0200
+Subject: [PATCH] mac80211: reduce duplication in tx status functions
+
+Move redundant functionality from __ieee80211_tx_status into
+ieee80211_tx_status_ext. Preparation for unifying with the 802.3 tx status
+codepath.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -184,18 +184,6 @@ static void ieee80211_frame_acked(struct
+ struct ieee80211_mgmt *mgmt = (void *) skb->data;
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+- struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+-
+- if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+- sta->status_stats.last_ack = jiffies;
+- if (txinfo->status.is_valid_ack_signal) {
+- sta->status_stats.last_ack_signal =
+- (s8)txinfo->status.ack_signal;
+- sta->status_stats.ack_signal_filled = true;
+- ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
+- -txinfo->status.ack_signal);
+- }
+- }
+
+ if (ieee80211_is_data_qos(mgmt->frame_control)) {
+ struct ieee80211_hdr *hdr = (void *) skb->data;
+@@ -899,7 +887,8 @@ void ieee80211_tx_monitor(struct ieee802
+ }
+
+ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
+- struct ieee80211_tx_status *status)
++ struct ieee80211_tx_status *status,
++ int rates_idx, int retry_count)
+ {
+ struct sk_buff *skb = status->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+@@ -908,8 +897,6 @@ static void __ieee80211_tx_status(struct
+ struct sta_info *sta;
+ __le16 fc;
+ struct ieee80211_supported_band *sband;
+- int retry_count;
+- int rates_idx;
+ bool send_to_cooked;
+ bool acked;
+ bool noack_success;
+@@ -918,8 +905,6 @@ static void __ieee80211_tx_status(struct
+ int tid = IEEE80211_NUM_TIDS;
+ u16 tx_time_est;
+
+- rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+-
+ sband = local->hw.wiphy->bands[info->band];
+ fc = hdr->frame_control;
+
+@@ -996,24 +981,14 @@ static void __ieee80211_tx_status(struct
+ if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+ ieee80211_handle_filtered_frame(local, sta, skb);
+ return;
+- } else {
++ } else if (ieee80211_is_data_present(fc)) {
+ if (!acked && !noack_success)
+- sta->status_stats.retry_failed++;
+- sta->status_stats.retry_count += retry_count;
+-
+- if (ieee80211_is_data_present(fc)) {
+- if (!acked && !noack_success)
+- sta->status_stats.msdu_failed[tid]++;
++ sta->status_stats.msdu_failed[tid]++;
+
+- sta->status_stats.msdu_retries[tid] +=
+- retry_count;
+- }
++ sta->status_stats.msdu_retries[tid] +=
++ retry_count;
+ }
+
+- rate_control_tx_status(local, sband, status);
+- if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+- ieee80211s_update_metric(local, sta, status);
+-
+ if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
+ ieee80211_frame_acked(sta, skb);
+
+@@ -1038,20 +1013,6 @@ static void __ieee80211_tx_status(struct
+ true);
+ ieee80211_info_set_tx_time_est(info, 0);
+ }
+-
+- if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+- if (acked) {
+- if (sta->status_stats.lost_packets)
+- sta->status_stats.lost_packets = 0;
+-
+- /* Track when last TDLS packet was ACKed */
+- sta->status_stats.last_pkt_time = jiffies;
+- } else if (noack_success) {
+- /* nothing to do here, do not account as lost */
+- } else {
+- ieee80211_lost_packet(sta, info);
+- }
+- }
+ }
+
+ /* SNMP counters
+@@ -1135,7 +1096,7 @@ void ieee80211_tx_status(struct ieee8021
+ if (sta)
+ status.sta = &sta->sta;
+
+- __ieee80211_tx_status(hw, &status);
++ ieee80211_tx_status_ext(hw, &status);
+ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status);
+@@ -1148,7 +1109,7 @@ void ieee80211_tx_status_ext(struct ieee
+ struct ieee80211_sta *pubsta = status->sta;
+ struct ieee80211_supported_band *sband;
+ struct sta_info *sta;
+- int retry_count;
++ int rates_idx, retry_count;
+ bool acked, noack_success;
+
+ if (pubsta) {
+@@ -1158,13 +1119,7 @@ void ieee80211_tx_status_ext(struct ieee
+ sta->tx_stats.last_rate_info = *status->rate;
+ }
+
+- if (status->skb)
+- return __ieee80211_tx_status(hw, status);
+-
+- if (!status->sta)
+- return;
+-
+- ieee80211_tx_get_rates(hw, info, &retry_count);
++ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+ sband = hw->wiphy->bands[info->band];
+
+@@ -1176,20 +1131,30 @@ void ieee80211_tx_status_ext(struct ieee
+ sta->status_stats.retry_failed++;
+ sta->status_stats.retry_count += retry_count;
+
+- if (acked) {
+- sta->status_stats.last_ack = jiffies;
++ if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
++ if (acked) {
++ sta->status_stats.last_ack = jiffies;
+
+- if (sta->status_stats.lost_packets)
+- sta->status_stats.lost_packets = 0;
++ if (sta->status_stats.lost_packets)
++ sta->status_stats.lost_packets = 0;
+
+- /* Track when last packet was ACKed */
+- sta->status_stats.last_pkt_time = jiffies;
+- } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+- return;
+- } else if (noack_success) {
+- /* nothing to do here, do not account as lost */
+- } else {
+- ieee80211_lost_packet(sta, info);
++ /* Track when last packet was ACKed */
++ sta->status_stats.last_pkt_time = jiffies;
++
++ if (info->status.is_valid_ack_signal) {
++ sta->status_stats.last_ack_signal =
++ (s8)info->status.ack_signal;
++ sta->status_stats.ack_signal_filled = true;
++ ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
++ -info->status.ack_signal);
++ }
++ } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
++ return;
++ } else if (noack_success) {
++ /* nothing to do here, do not account as lost */
++ } else {
++ ieee80211_lost_packet(sta, info);
++ }
+ }
+
+ rate_control_tx_status(local, sband, status);
+@@ -1197,6 +1162,10 @@ void ieee80211_tx_status_ext(struct ieee
+ ieee80211s_update_metric(local, sta, status);
+ }
+
++ if (status->skb)
++ return __ieee80211_tx_status(hw, status, rates_idx,
++ retry_count);
++
+ if (acked || noack_success) {
+ I802_DEBUG_INC(local->dot11TransmittedFrameCount);
+ if (!pubsta)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 13:54:19 +0200
-Subject: [PATCH] mac80211: unify 802.3 (offload) and 802.11 tx status
- codepath
-
-Make ieee80211_tx_status_8023 call ieee80211_tx_status_ext, similar to
-ieee80211_tx_status.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -903,7 +903,6 @@ static void __ieee80211_tx_status(struct
- struct ieee80211_bar *bar;
- int shift = 0;
- int tid = IEEE80211_NUM_TIDS;
-- u16 tx_time_est;
-
- sband = local->hw.wiphy->bands[info->band];
- fc = hdr->frame_control;
-@@ -996,17 +995,6 @@ static void __ieee80211_tx_status(struct
- ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
- ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
- acked, info->status.tx_time);
--
-- if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
-- /* Do this here to avoid the expensive lookup of the sta
-- * in ieee80211_report_used_skb().
-- */
-- ieee80211_sta_update_pending_airtime(local, sta,
-- skb_get_queue_mapping(skb),
-- tx_time_est,
-- true);
-- ieee80211_info_set_tx_time_est(info, 0);
-- }
- }
-
- /* SNMP counters
-@@ -1102,9 +1090,11 @@ void ieee80211_tx_status_ext(struct ieee
- struct ieee80211_tx_info *info = status->info;
- struct ieee80211_sta *pubsta = status->sta;
- struct ieee80211_supported_band *sband;
-- struct sta_info *sta;
-+ struct sk_buff *skb = status->skb;
-+ struct sta_info *sta = NULL;
- int rates_idx, retry_count;
- bool acked, noack_success;
-+ u16 tx_time_est;
-
- if (pubsta) {
- sta = container_of(pubsta, struct sta_info, sta);
-@@ -1156,7 +1146,18 @@ void ieee80211_tx_status_ext(struct ieee
- ieee80211s_update_metric(local, sta, status);
- }
-
-- if (status->skb)
-+ if (skb && (tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
-+ /* Do this here to avoid the expensive lookup of the sta
-+ * in ieee80211_report_used_skb().
-+ */
-+ ieee80211_sta_update_pending_airtime(local, sta,
-+ skb_get_queue_mapping(skb),
-+ tx_time_est,
-+ true);
-+ ieee80211_info_set_tx_time_est(info, 0);
-+ }
-+
-+ if (skb && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
- return __ieee80211_tx_status(hw, status, rates_idx,
- retry_count);
-
-@@ -1171,6 +1172,12 @@ void ieee80211_tx_status_ext(struct ieee
- } else {
- I802_DEBUG_INC(local->dot11FailedCount);
- }
-+
-+ if (!skb)
-+ return;
-+
-+ ieee80211_report_used_skb(local, skb, false);
-+ dev_kfree_skb(skb);
- }
- EXPORT_SYMBOL(ieee80211_tx_status_ext);
-
-@@ -1197,66 +1204,23 @@ void ieee80211_tx_status_8023(struct iee
- struct ieee80211_vif *vif,
- struct sk_buff *skb)
- {
-- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata;
-- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ struct ieee80211_tx_status status = {
-+ .skb = skb,
-+ .info = IEEE80211_SKB_CB(skb),
-+ };
- struct sta_info *sta;
-- int retry_count;
-- int rates_idx;
-- bool acked;
-
- sdata = vif_to_sdata(vif);
-
-- acked = info->flags & IEEE80211_TX_STAT_ACK;
-- rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
--
- rcu_read_lock();
-
-- if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
-- goto counters_update;
--
-- if (IS_ERR(sta))
-- goto counters_update;
--
-- if (!acked)
-- sta->status_stats.retry_failed++;
--
-- if (rates_idx != -1)
-- sta->tx_stats.last_rate = info->status.rates[rates_idx];
--
-- sta->status_stats.retry_count += retry_count;
--
-- if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
-- sta->status_stats.last_ack = jiffies;
-- if (info->flags & IEEE80211_TX_STAT_ACK) {
-- if (sta->status_stats.lost_packets)
-- sta->status_stats.lost_packets = 0;
-+ if (!ieee80211_lookup_ra_sta(sdata, skb, &sta) && !IS_ERR(sta))
-+ status.sta = &sta->sta;
-
-- sta->status_stats.last_pkt_time = jiffies;
-- } else {
-- ieee80211_lost_packet(sta, info);
-- }
-- }
-+ ieee80211_tx_status_ext(hw, &status);
-
--counters_update:
- rcu_read_unlock();
-- ieee80211_led_tx(local);
--
-- if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
-- !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
-- goto skip_stats_update;
--
-- I802_DEBUG_INC(local->dot11TransmittedFrameCount);
-- if (is_multicast_ether_addr(skb->data))
-- I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
-- if (retry_count > 0)
-- I802_DEBUG_INC(local->dot11RetryCount);
-- if (retry_count > 1)
-- I802_DEBUG_INC(local->dot11MultipleRetryCount);
--
--skip_stats_update:
-- ieee80211_report_used_skb(local, skb, false);
-- dev_kfree_skb(skb);
- }
- EXPORT_SYMBOL(ieee80211_tx_status_8023);
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 13:55:56 +0200
-Subject: [PATCH] mac80211: add missing queue/hash initialization to 802.3
- xmit
-
-Fixes AQL for encap-offloaded tx
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4200,6 +4200,12 @@ static void ieee80211_8023_xmit(struct i
- if (is_zero_ether_addr(ra))
- goto out_free;
-
-+ if (local->ops->wake_tx_queue) {
-+ u16 queue = __ieee80211_select_queue(sdata, sta, skb);
-+ skb_set_queue_mapping(skb, queue);
-+ skb_get_hash(skb);
-+ }
-+
- multicast = is_multicast_ether_addr(ra);
-
- if (sta)
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 13:29:12 +0200
+Subject: [PATCH] mac80211: remove tx status call to
+ ieee80211_sta_register_airtime
+
+All drivers using airtime fairness are calling ieee80211_sta_register_airtime
+directly
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -997,12 +997,6 @@ static void __ieee80211_tx_status(struct
+ ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
+ acked, info->status.tx_time);
+
+- if (info->status.tx_time &&
+- wiphy_ext_feature_isset(local->hw.wiphy,
+- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+- ieee80211_sta_register_airtime(&sta->sta, tid,
+- info->status.tx_time, 0);
+-
+ if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
+ /* Do this here to avoid the expensive lookup of the sta
+ * in ieee80211_report_used_skb().
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 17 Aug 2020 21:11:25 +0200
-Subject: [PATCH] mac80211: check and refresh aggregation session in encap
- offload tx
-
-Update the last_tx timestamp to avoid tearing down the aggregation session
-early. Fall back to the slow path if the session setup is still running
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4189,6 +4189,8 @@ static void ieee80211_8023_xmit(struct i
- bool authorized = false;
- bool multicast;
- unsigned char *ra = ehdr->h_dest;
-+ struct tid_ampdu_tx *tid_tx;
-+ u8 tid;
-
- if (IS_ERR(sta) || (sta && !sta->uploaded))
- sta = NULL;
-@@ -4226,6 +4228,22 @@ static void ieee80211_8023_xmit(struct i
-
- memset(info, 0, sizeof(*info));
-
-+ if (sta) {
-+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-+ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-+ if (tid_tx) {
-+ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
-+ /* fall back to non-offload slow path */
-+ __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
-+ return;
-+ }
-+
-+ info->flags |= IEEE80211_TX_CTL_AMPDU;
-+ if (tid_tx->timeout)
-+ tid_tx->last_tx = jiffies;
-+ }
-+ }
-+
- if (unlikely(!multicast && skb->sk &&
- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 13:29:56 +0200
+Subject: [PATCH] mac80211: optimize station connection monitor
+
+Calling mod_timer for every rx/tx packet can be quite expensive.
+Instead of constantly updating the timer, we can simply let it run out
+and check the timestamp of the last ACK or rx packet to re-arm it.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2045,8 +2045,6 @@ void ieee80211_dynamic_ps_timer(struct t
+ void ieee80211_send_nullfunc(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ bool powersave);
+-void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+- struct ieee80211_hdr *hdr);
+ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
+
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -2432,23 +2432,6 @@ static void ieee80211_set_disassoc(struc
+ sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+ }
+
+-void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+- struct ieee80211_hdr *hdr)
+-{
+- /*
+- * We can postpone the mgd.timer whenever receiving unicast frames
+- * from AP because we know that the connection is working both ways
+- * at that time. But multicast frames (and hence also beacons) must
+- * be ignored here, because we need to trigger the timer during
+- * data idle periods for sending the periodic probe request to the
+- * AP we're connected to.
+- */
+- if (is_multicast_ether_addr(hdr->addr1))
+- return;
+-
+- ieee80211_sta_reset_conn_monitor(sdata);
+-}
+-
+ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
+ {
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+@@ -2521,21 +2504,13 @@ void ieee80211_sta_tx_notify(struct ieee
+ {
+ ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
+
+- if (!ieee80211_is_data(hdr->frame_control))
+- return;
+-
+- if (ieee80211_is_any_nullfunc(hdr->frame_control) &&
+- sdata->u.mgd.probe_send_count > 0) {
+- if (ack)
+- ieee80211_sta_reset_conn_monitor(sdata);
+- else
+- sdata->u.mgd.nullfunc_failed = true;
+- ieee80211_queue_work(&sdata->local->hw, &sdata->work);
++ if (!ieee80211_is_any_nullfunc(hdr->frame_control) ||
++ !sdata->u.mgd.probe_send_count)
+ return;
+- }
+
+- if (ack)
+- ieee80211_sta_reset_conn_monitor(sdata);
++ if (!ack)
++ sdata->u.mgd.nullfunc_failed = true;
++ ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+ }
+
+ static void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata,
+@@ -3600,8 +3575,8 @@ static bool ieee80211_assoc_success(stru
+ * Start timer to probe the connection to the AP now.
+ * Also start the timer that will detect beacon loss.
+ */
+- ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
+ ieee80211_sta_reset_beacon_monitor(sdata);
++ ieee80211_sta_reset_conn_monitor(sdata);
+
+ ret = true;
+ out:
+@@ -4569,10 +4544,26 @@ static void ieee80211_sta_conn_mon_timer
+ from_timer(sdata, t, u.mgd.conn_mon_timer);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
++ struct sta_info *sta;
++ unsigned long timeout;
+
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
+ return;
+
++ sta = sta_info_get(sdata, ifmgd->bssid);
++ if (!sta)
++ return;
++
++ timeout = sta->status_stats.last_ack;
++ if (time_before(sta->status_stats.last_ack, sta->rx_stats.last_rx))
++ timeout = sta->rx_stats.last_rx;
++ timeout += IEEE80211_CONNECTION_IDLE_TIME;
++
++ if (time_is_before_jiffies(timeout)) {
++ mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout));
++ return;
++ }
++
+ ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
+ }
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1811,9 +1811,6 @@ ieee80211_rx_h_sta_process(struct ieee80
+ sta->rx_stats.last_rate = sta_stats_encode_rate(status);
+ }
+
+- if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
+- ieee80211_sta_rx_notify(rx->sdata, hdr);
+-
+ sta->rx_stats.fragments++;
+
+ u64_stats_update_begin(&rx->sta->rx_stats.syncp);
+@@ -4148,7 +4145,6 @@ void ieee80211_check_fast_rx(struct sta_
+ fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
+ fastrx.expected_ds_bits = 0;
+ } else {
+- fastrx.sta_notify = sdata->u.mgd.probe_send_count > 0;
+ fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
+ fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr3);
+ fastrx.expected_ds_bits =
+@@ -4378,11 +4374,6 @@ static bool ieee80211_invoke_fast_rx(str
+ pskb_trim(skb, skb->len - fast_rx->icv_len))
+ goto drop;
+
+- if (unlikely(fast_rx->sta_notify)) {
+- ieee80211_sta_rx_notify(rx->sdata, hdr);
+- fast_rx->sta_notify = false;
+- }
+-
+ /* statistics part of ieee80211_rx_h_sta_process() */
+ if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
+ stats->last_signal = status->signal;
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -336,7 +336,6 @@ struct ieee80211_fast_tx {
+ * @expected_ds_bits: from/to DS bits expected
+ * @icv_len: length of the MIC if present
+ * @key: bool indicating encryption is expected (key is set)
+- * @sta_notify: notify the MLME code (once)
+ * @internal_forward: forward froms internally on AP/VLAN type interfaces
+ * @uses_rss: copy of USES_RSS hw flag
+ * @da_offs: offset of the DA in the header (for header conversion)
+@@ -352,7 +351,6 @@ struct ieee80211_fast_rx {
+ __le16 expected_ds_bits;
+ u8 icv_len;
+ u8 key:1,
+- sta_notify:1,
+ internal_forward:1,
+ uses_rss:1;
+ u8 da_offs, sa_offs;
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1227,9 +1227,6 @@ void ieee80211_tx_status_8023(struct iee
+ sta->status_stats.retry_count += retry_count;
+
+ if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
+- if (acked && vif->type == NL80211_IFTYPE_STATION)
+- ieee80211_sta_reset_conn_monitor(sdata);
+-
+ sta->status_stats.last_ack = jiffies;
+ if (info->flags & IEEE80211_TX_STAT_ACK) {
+ if (sta->status_stats.lost_packets)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 20 Aug 2020 17:27:00 +0200
-Subject: [PATCH] mac80211: support using ieee80211_tx_status_ext to free
- skbs without status info
-
-For encap-offloaded packets, ieee80211_free_txskb cannot be used, since it
-does not have the vif pointer.
-Using ieee80211_tx_status_ext for this purpose has the advantage of being able
-avoid an extra station lookup for AQL
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -1103,6 +1103,21 @@ void ieee80211_tx_status_ext(struct ieee
- sta->tx_stats.last_rate_info = *status->rate;
- }
-
-+ if (skb && (tx_time_est =
-+ ieee80211_info_get_tx_time_est(IEEE80211_SKB_CB(skb))) > 0) {
-+ /* Do this here to avoid the expensive lookup of the sta
-+ * in ieee80211_report_used_skb().
-+ */
-+ ieee80211_sta_update_pending_airtime(local, sta,
-+ skb_get_queue_mapping(skb),
-+ tx_time_est,
-+ true);
-+ ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
-+ }
-+
-+ if (!status->info)
-+ goto free;
-+
- rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
-
- sband = hw->wiphy->bands[info->band];
-@@ -1146,17 +1161,6 @@ void ieee80211_tx_status_ext(struct ieee
- ieee80211s_update_metric(local, sta, status);
- }
-
-- if (skb && (tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
-- /* Do this here to avoid the expensive lookup of the sta
-- * in ieee80211_report_used_skb().
-- */
-- ieee80211_sta_update_pending_airtime(local, sta,
-- skb_get_queue_mapping(skb),
-- tx_time_est,
-- true);
-- ieee80211_info_set_tx_time_est(info, 0);
-- }
--
- if (skb && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
- return __ieee80211_tx_status(hw, status, rates_idx,
- retry_count);
-@@ -1173,6 +1177,7 @@ void ieee80211_tx_status_ext(struct ieee
- I802_DEBUG_INC(local->dot11FailedCount);
- }
-
-+free:
- if (!skb)
- return;
-
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 13:35:32 +0200
+Subject: [PATCH] mac80211: swap NEED_TXPROCESSING and HW_80211_ENCAP tx
+ flags
+
+In order to unify the tx status path, the hw 802.11 encapsulation flag
+needs to survive the trip to the tx status call.
+Since we don't have any free bits in info->flags, we need to move one.
+IEEE80211_TX_INTFL_NEED_TXPROCESSING is only used internally in mac80211,
+and only before the call into the driver.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
+@@ -14,7 +14,7 @@ ath11k_dp_tx_get_encap_type(struct ath11
+ {
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+
+- if (tx_info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
++ if (tx_info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
+ return HAL_TCL_ENCAP_TYPE_ETHERNET;
+
+ return HAL_TCL_ENCAP_TYPE_NATIVE_WIFI;
+@@ -93,7 +93,7 @@ int ath11k_dp_tx(struct ath11k *ar, stru
+ if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
+ return -ESHUTDOWN;
+
+- if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) &&
++ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+ !ieee80211_is_data(hdr->frame_control))
+ return -ENOTSUPP;
+
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -3749,7 +3749,7 @@ static int ath11k_mac_mgmt_tx_wmi(struct
+ return -ENOSPC;
+
+ info = IEEE80211_SKB_CB(skb);
+- if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)) {
++ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
+ if ((ieee80211_is_action(hdr->frame_control) ||
+ ieee80211_is_deauth(hdr->frame_control) ||
+ ieee80211_is_disassoc(hdr->frame_control)) &&
+@@ -3876,7 +3876,7 @@ static void ath11k_mac_op_tx(struct ieee
+ bool is_prb_rsp;
+ int ret;
+
+- if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) {
++ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
+ skb_cb->flags |= ATH11K_SKB_HW_80211_ENCAP;
+ } else if (ieee80211_is_mgmt(hdr->frame_control)) {
+ is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -720,9 +720,8 @@ struct ieee80211_bss_conf {
+ * @IEEE80211_TX_INTFL_OFFCHAN_TX_OK: Internal to mac80211. Used to indicate
+ * that a frame can be transmitted while the queues are stopped for
+ * off-channel operation.
+- * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
+- * used to indicate that a pending frame requires TX processing before
+- * it can be sent out.
++ * @IEEE80211_TX_CTL_HW_80211_ENCAP: This frame uses hardware encapsulation
++ * (header conversion)
+ * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211,
+ * used to indicate that a frame was already retried due to PS
+ * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211,
+@@ -791,7 +790,7 @@ enum mac80211_tx_info_flags {
+ IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11),
+ IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12),
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK = BIT(13),
+- IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14),
++ IEEE80211_TX_CTL_HW_80211_ENCAP = BIT(14),
+ IEEE80211_TX_INTFL_RETRIED = BIT(15),
+ IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16),
+ IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17),
+@@ -823,8 +822,9 @@ enum mac80211_tx_info_flags {
+ * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
+ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
+ * @IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP: This frame skips mesh path lookup
+- * @IEEE80211_TX_CTRL_HW_80211_ENCAP: This frame uses hardware encapsulation
+- * (header conversion)
++ * @IEEE80211_TX_INTCFL_NEED_TXPROCESSING: completely internal to mac80211,
++ * used to indicate that a pending frame requires TX processing before
++ * it can be sent out.
+ *
+ * These flags are used in tx_info->control.flags.
+ */
+@@ -835,7 +835,7 @@ enum mac80211_tx_control_flags {
+ IEEE80211_TX_CTRL_AMSDU = BIT(3),
+ IEEE80211_TX_CTRL_FAST_XMIT = BIT(4),
+ IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP = BIT(5),
+- IEEE80211_TX_CTRL_HW_80211_ENCAP = BIT(6),
++ IEEE80211_TX_INTCFL_NEED_TXPROCESSING = BIT(6),
+ };
+
+ /*
+--- a/net/mac80211/mesh_hwmp.c
++++ b/net/mac80211/mesh_hwmp.c
+@@ -212,7 +212,7 @@ static void prepare_frame_for_deferred_t
+ skb->priority = 7;
+
+ info->control.vif = &sdata->vif;
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ ieee80211_set_qos_hdr(sdata, skb);
+ ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
+ }
+@@ -1163,7 +1163,7 @@ int mesh_nexthop_resolve(struct ieee8021
+ if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN)
+ skb_to_free = skb_dequeue(&mpath->frame_queue);
+
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ ieee80211_set_qos_hdr(sdata, skb);
+ skb_queue_tail(&mpath->frame_queue, skb);
+ if (skb_to_free)
+--- a/net/mac80211/mesh_ps.c
++++ b/net/mac80211/mesh_ps.c
+@@ -432,7 +432,7 @@ static void mpsp_qos_null_append(struct
+
+ info = IEEE80211_SKB_CB(new_skb);
+ info->control.vif = &sdata->vif;
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+
+ __skb_queue_tail(frames, new_skb);
+ }
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2896,7 +2896,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
+ fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
+ info = IEEE80211_SKB_CB(fwd_skb);
+ memset(info, 0, sizeof(*info));
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ info->control.vif = &rx->sdata->vif;
+ info->control.jiffies = jiffies;
+ if (is_multicast_ether_addr(fwd_hdr->addr1)) {
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -66,8 +66,8 @@ static void ieee80211_handle_filtered_fr
+
+ info->control.jiffies = jiffies;
+ info->control.vif = &sta->sdata->vif;
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING |
+- IEEE80211_TX_INTFL_RETRANSMISSION;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
++ info->flags |= IEEE80211_TX_INTFL_RETRANSMISSION;
+ info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
+
+ sta->status_stats.filtered++;
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -530,7 +530,7 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
+
+ info->control.jiffies = jiffies;
+ info->control.vif = &tx->sdata->vif;
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
+ skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
+ spin_unlock(&sta->ps_lock);
+@@ -1132,7 +1132,7 @@ static bool ieee80211_tx_prep_agg(struct
+ tx->sta->sta.addr, tx->sta->sta.aid);
+ }
+ info->control.vif = &tx->sdata->vif;
+- info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
+ __skb_queue_tail(&tid_tx->pending, skb);
+ if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)
+@@ -1177,7 +1177,7 @@ ieee80211_tx_prepare(struct ieee80211_su
+ * we are doing the needed processing, so remove the flag
+ * now.
+ */
+- info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++ info->control.flags &= ~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+@@ -1256,7 +1256,7 @@ static struct txq_info *ieee80211_get_tx
+ (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
+ return NULL;
+
+- if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) &&
++ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+ unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
+ if ((!ieee80211_is_mgmt(hdr->frame_control) ||
+ ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
+@@ -3640,7 +3640,7 @@ begin:
+ else
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+- if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
++ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
+ goto encap_out;
+
+ if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
+@@ -4230,7 +4230,7 @@ static void ieee80211_8023_xmit(struct i
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+
+- info->control.flags |= IEEE80211_TX_CTRL_HW_80211_ENCAP;
++ info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
+ info->control.vif = &sdata->vif;
+
+ if (key)
+@@ -4355,7 +4355,7 @@ static bool ieee80211_tx_pending_skb(str
+
+ sdata = vif_to_sdata(info->control.vif);
+
+- if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
++ if (info->control.flags & IEEE80211_TX_INTCFL_NEED_TXPROCESSING) {
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (unlikely(!chanctx_conf)) {
+ dev_kfree_skb(skb);
+@@ -4363,7 +4363,7 @@ static bool ieee80211_tx_pending_skb(str
+ }
+ info->band = chanctx_conf->def.chan->band;
+ result = ieee80211_tx(sdata, NULL, skb, true, 0);
+- } else if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) {
++ } else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ dev_kfree_skb(skb);
+ return true;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 21 Aug 2020 05:49:07 +0200
-Subject: [PATCH] mac80211: extend ieee80211_tx_status_ext to support bulk
- free
-
-Store processed skbs ready to be freed in a list so the driver bulk free them
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1092,12 +1092,14 @@ ieee80211_info_get_tx_time_est(struct ie
- * @info: Basic tx status information
- * @skb: Packet skb (can be NULL if not provided by the driver)
- * @rate: The TX rate that was used when sending the packet
-+ * @free_list: list where processed skbs are stored to be free'd by the driver
- */
- struct ieee80211_tx_status {
- struct ieee80211_sta *sta;
- struct ieee80211_tx_info *info;
- struct sk_buff *skb;
- struct rate_info *rate;
-+ struct list_head *free_list;
- };
-
- /**
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -1053,7 +1053,10 @@ static void __ieee80211_tx_status(struct
- * with this test...
- */
- if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) {
-- dev_kfree_skb(skb);
-+ if (status->free_list)
-+ list_add_tail(&skb->list, status->free_list);
-+ else
-+ dev_kfree_skb(skb);
- return;
- }
-
-@@ -1182,7 +1185,10 @@ free:
- return;
-
- ieee80211_report_used_skb(local, skb, false);
-- dev_kfree_skb(skb);
-+ if (status->free_list)
-+ list_add_tail(&skb->list, status->free_list);
-+ else
-+ dev_kfree_skb(skb);
- }
- EXPORT_SYMBOL(ieee80211_tx_status_ext);
-
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 17 Aug 2020 13:54:19 +0200
+Subject: [PATCH] mac80211: unify 802.3 (offload) and 802.11 tx status
+ codepath
+
+Make ieee80211_tx_status_8023 call ieee80211_tx_status_ext, similar to
+ieee80211_tx_status.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -903,7 +903,6 @@ static void __ieee80211_tx_status(struct
+ struct ieee80211_bar *bar;
+ int shift = 0;
+ int tid = IEEE80211_NUM_TIDS;
+- u16 tx_time_est;
+
+ sband = local->hw.wiphy->bands[info->band];
+ fc = hdr->frame_control;
+@@ -996,17 +995,6 @@ static void __ieee80211_tx_status(struct
+ ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
+ ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
+ acked, info->status.tx_time);
+-
+- if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
+- /* Do this here to avoid the expensive lookup of the sta
+- * in ieee80211_report_used_skb().
+- */
+- ieee80211_sta_update_pending_airtime(local, sta,
+- skb_get_queue_mapping(skb),
+- tx_time_est,
+- true);
+- ieee80211_info_set_tx_time_est(info, 0);
+- }
+ }
+
+ /* SNMP counters
+@@ -1102,9 +1090,11 @@ void ieee80211_tx_status_ext(struct ieee
+ struct ieee80211_tx_info *info = status->info;
+ struct ieee80211_sta *pubsta = status->sta;
+ struct ieee80211_supported_band *sband;
+- struct sta_info *sta;
++ struct sk_buff *skb = status->skb;
++ struct sta_info *sta = NULL;
+ int rates_idx, retry_count;
+ bool acked, noack_success;
++ u16 tx_time_est;
+
+ if (pubsta) {
+ sta = container_of(pubsta, struct sta_info, sta);
+@@ -1156,7 +1146,18 @@ void ieee80211_tx_status_ext(struct ieee
+ ieee80211s_update_metric(local, sta, status);
+ }
+
+- if (status->skb)
++ if (skb && (tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
++ /* Do this here to avoid the expensive lookup of the sta
++ * in ieee80211_report_used_skb().
++ */
++ ieee80211_sta_update_pending_airtime(local, sta,
++ skb_get_queue_mapping(skb),
++ tx_time_est,
++ true);
++ ieee80211_info_set_tx_time_est(info, 0);
++ }
++
++ if (skb && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
+ return __ieee80211_tx_status(hw, status, rates_idx,
+ retry_count);
+
+@@ -1171,6 +1172,12 @@ void ieee80211_tx_status_ext(struct ieee
+ } else {
+ I802_DEBUG_INC(local->dot11FailedCount);
+ }
++
++ if (!skb)
++ return;
++
++ ieee80211_report_used_skb(local, skb, false);
++ dev_kfree_skb(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status_ext);
+
+@@ -1197,66 +1204,23 @@ void ieee80211_tx_status_8023(struct iee
+ struct ieee80211_vif *vif,
+ struct sk_buff *skb)
+ {
+- struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ struct ieee80211_tx_status status = {
++ .skb = skb,
++ .info = IEEE80211_SKB_CB(skb),
++ };
+ struct sta_info *sta;
+- int retry_count;
+- int rates_idx;
+- bool acked;
+
+ sdata = vif_to_sdata(vif);
+
+- acked = info->flags & IEEE80211_TX_STAT_ACK;
+- rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+-
+ rcu_read_lock();
+
+- if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
+- goto counters_update;
+-
+- if (IS_ERR(sta))
+- goto counters_update;
+-
+- if (!acked)
+- sta->status_stats.retry_failed++;
+-
+- if (rates_idx != -1)
+- sta->tx_stats.last_rate = info->status.rates[rates_idx];
+-
+- sta->status_stats.retry_count += retry_count;
+-
+- if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
+- sta->status_stats.last_ack = jiffies;
+- if (info->flags & IEEE80211_TX_STAT_ACK) {
+- if (sta->status_stats.lost_packets)
+- sta->status_stats.lost_packets = 0;
++ if (!ieee80211_lookup_ra_sta(sdata, skb, &sta) && !IS_ERR(sta))
++ status.sta = &sta->sta;
+
+- sta->status_stats.last_pkt_time = jiffies;
+- } else {
+- ieee80211_lost_packet(sta, info);
+- }
+- }
++ ieee80211_tx_status_ext(hw, &status);
+
+-counters_update:
+ rcu_read_unlock();
+- ieee80211_led_tx(local);
+-
+- if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
+- !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
+- goto skip_stats_update;
+-
+- I802_DEBUG_INC(local->dot11TransmittedFrameCount);
+- if (is_multicast_ether_addr(skb->data))
+- I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
+- if (retry_count > 0)
+- I802_DEBUG_INC(local->dot11RetryCount);
+- if (retry_count > 1)
+- I802_DEBUG_INC(local->dot11MultipleRetryCount);
+-
+-skip_stats_update:
+- ieee80211_report_used_skb(local, skb, false);
+- dev_kfree_skb(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status_8023);
+
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 21 Aug 2020 05:51:58 +0200
-Subject: [PATCH] mac80211: notify the driver when a sta uses 4-address
- mode
-
-This is needed for encapsulation offload of 4-address mode packets
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -3840,6 +3840,8 @@ enum ieee80211_reconfig_type {
- * This callback may sleep.
- * @update_vif_config: Update virtual interface offload flags
- * This callback may sleep.
-+ * @sta_set_4addr: Called to notify the driver when a station starts/stops using
-+ * 4-address mode
- */
- struct ieee80211_ops {
- void (*tx)(struct ieee80211_hw *hw,
-@@ -4153,6 +4155,8 @@ struct ieee80211_ops {
- struct ieee80211_sta *sta, u8 tids);
- void (*update_vif_offload)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif);
-+ void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-+ struct ieee80211_sta *sta, bool enabled);
- };
-
- /**
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1698,6 +1698,7 @@ static int ieee80211_change_station(stru
-
- rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
- __ieee80211_check_fast_rx_iface(vlansdata);
-+ drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
- }
-
- if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
---- a/net/mac80211/driver-ops.h
-+++ b/net/mac80211/driver-ops.h
-@@ -1400,4 +1400,18 @@ static inline void drv_update_vif_offloa
- trace_drv_return_void(local);
- }
-
-+static inline void drv_sta_set_4addr(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_sta *sta, bool enabled)
-+{
-+ sdata = get_bss_sdata(sdata);
-+ if (!check_sdata_in_driver(sdata))
-+ return;
-+
-+ trace_drv_sta_set_4addr(local, sdata, sta, enabled);
-+ if (local->ops->sta_set_4addr)
-+ local->ops->sta_set_4addr(&local->hw, &sdata->vif, sta, enabled);
-+ trace_drv_return_void(local);
-+}
-+
- #endif /* __MAC80211_DRIVER_OPS */
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -3518,6 +3518,9 @@ static bool ieee80211_assoc_success(stru
- goto out;
- }
-
-+ if (sdata->wdev.use_4addr)
-+ drv_sta_set_4addr(local, sdata, &sta->sta, true);
-+
- mutex_unlock(&sdata->local->sta_mtx);
-
- /*
---- a/net/mac80211/trace.h
-+++ b/net/mac80211/trace.h
-@@ -2739,6 +2739,33 @@ DEFINE_EVENT(local_sdata_addr_evt, drv_u
- TP_ARGS(local, sdata)
- );
-
-+TRACE_EVENT(drv_sta_set_4addr,
-+ TP_PROTO(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_sta *sta, bool enabled),
-+
-+ TP_ARGS(local, sdata, sta, enabled),
-+
-+ TP_STRUCT__entry(
-+ LOCAL_ENTRY
-+ VIF_ENTRY
-+ STA_ENTRY
-+ __field(bool, enabled)
-+ ),
-+
-+ TP_fast_assign(
-+ LOCAL_ASSIGN;
-+ VIF_ASSIGN;
-+ STA_ASSIGN;
-+ __entry->enabled = enabled;
-+ ),
-+
-+ TP_printk(
-+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " enabled:%d",
-+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->enabled
-+ )
-+);
-+
- #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 20 Aug 2020 17:27:00 +0200
+Subject: [PATCH] mac80211: support using ieee80211_tx_status_ext to free
+ skbs without status info
+
+For encap-offloaded packets, ieee80211_free_txskb cannot be used, since it
+does not have the vif pointer.
+Using ieee80211_tx_status_ext for this purpose has the advantage of being able
+avoid an extra station lookup for AQL
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1103,6 +1103,21 @@ void ieee80211_tx_status_ext(struct ieee
+ sta->tx_stats.last_rate_info = *status->rate;
+ }
+
++ if (skb && (tx_time_est =
++ ieee80211_info_get_tx_time_est(IEEE80211_SKB_CB(skb))) > 0) {
++ /* Do this here to avoid the expensive lookup of the sta
++ * in ieee80211_report_used_skb().
++ */
++ ieee80211_sta_update_pending_airtime(local, sta,
++ skb_get_queue_mapping(skb),
++ tx_time_est,
++ true);
++ ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
++ }
++
++ if (!status->info)
++ goto free;
++
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+ sband = hw->wiphy->bands[info->band];
+@@ -1146,17 +1161,6 @@ void ieee80211_tx_status_ext(struct ieee
+ ieee80211s_update_metric(local, sta, status);
+ }
+
+- if (skb && (tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
+- /* Do this here to avoid the expensive lookup of the sta
+- * in ieee80211_report_used_skb().
+- */
+- ieee80211_sta_update_pending_airtime(local, sta,
+- skb_get_queue_mapping(skb),
+- tx_time_est,
+- true);
+- ieee80211_info_set_tx_time_est(info, 0);
+- }
+-
+ if (skb && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
+ return __ieee80211_tx_status(hw, status, rates_idx,
+ retry_count);
+@@ -1173,6 +1177,7 @@ void ieee80211_tx_status_ext(struct ieee
+ I802_DEBUG_INC(local->dot11FailedCount);
+ }
+
++free:
+ if (!skb)
+ return;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 21 Aug 2020 05:49:07 +0200
+Subject: [PATCH] mac80211: extend ieee80211_tx_status_ext to support
+ bulk free
+
+Store processed skbs ready to be freed in a list so the driver bulk free them
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1092,12 +1092,14 @@ ieee80211_info_get_tx_time_est(struct ie
+ * @info: Basic tx status information
+ * @skb: Packet skb (can be NULL if not provided by the driver)
+ * @rate: The TX rate that was used when sending the packet
++ * @free_list: list where processed skbs are stored to be free'd by the driver
+ */
+ struct ieee80211_tx_status {
+ struct ieee80211_sta *sta;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb;
+ struct rate_info *rate;
++ struct list_head *free_list;
+ };
+
+ /**
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1053,7 +1053,10 @@ static void __ieee80211_tx_status(struct
+ * with this test...
+ */
+ if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) {
+- dev_kfree_skb(skb);
++ if (status->free_list)
++ list_add_tail(&skb->list, status->free_list);
++ else
++ dev_kfree_skb(skb);
+ return;
+ }
+
+@@ -1182,7 +1185,10 @@ free:
+ return;
+
+ ieee80211_report_used_skb(local, skb, false);
+- dev_kfree_skb(skb);
++ if (status->free_list)
++ list_add_tail(&skb->list, status->free_list);
++ else
++ dev_kfree_skb(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status_ext);
+
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 21 Aug 2020 05:54:10 +0200
-Subject: [PATCH] mac80211: skip encap offload for tx multicast/control
- packets
-
-This simplifies the checks in the encap offload tx handler and allows using
-it in cases where software crypto is used for multicast packets, e.g. when
-using an AP_VLAN.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -378,7 +378,8 @@ static bool ieee80211_set_sdata_offload_
- if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
- key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
- key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
-- key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256 ||
-+ !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE))
- continue;
- if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP ||
- !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-@@ -1448,7 +1449,8 @@ static void ieee80211_set_vif_encap_ops(
- if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
- key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
- key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
-- key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
-+ key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256 ||
-+ !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE))
- continue;
- if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
- enabled = false;
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4184,88 +4184,47 @@ static void ieee80211_8023_xmit(struct i
- struct sk_buff *skb)
- {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-- struct ethhdr *ehdr = (struct ethhdr *)skb->data;
- struct ieee80211_local *local = sdata->local;
-- bool authorized = false;
-- bool multicast;
-- unsigned char *ra = ehdr->h_dest;
- struct tid_ampdu_tx *tid_tx;
- u8 tid;
-
-- if (IS_ERR(sta) || (sta && !sta->uploaded))
-- sta = NULL;
--
-- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-- (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER)))
-- ra = sdata->u.mgd.bssid;
--
-- if (is_zero_ether_addr(ra))
-- goto out_free;
--
- if (local->ops->wake_tx_queue) {
- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
- skb_set_queue_mapping(skb, queue);
- skb_get_hash(skb);
- }
-
-- multicast = is_multicast_ether_addr(ra);
--
-- if (sta)
-- authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
--
-- if (!multicast && !authorized &&
-- (ehdr->h_proto != sdata->control_port_protocol ||
-- !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
-- goto out_free;
--
-- if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
-- !atomic_read(&sdata->u.ap.num_mcast_sta))
-- goto out_free;
--
- if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
- test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
- goto out_free;
-
- memset(info, 0, sizeof(*info));
-
-- if (sta) {
-- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-- if (tid_tx) {
-- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
-- /* fall back to non-offload slow path */
-- __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
-- return;
-- }
--
-- info->flags |= IEEE80211_TX_CTL_AMPDU;
-- if (tid_tx->timeout)
-- tid_tx->last_tx = jiffies;
-+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-+ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-+ if (tid_tx) {
-+ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
-+ /* fall back to non-offload slow path */
-+ __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
-+ return;
- }
-+
-+ info->flags |= IEEE80211_TX_CTL_AMPDU;
-+ if (tid_tx->timeout)
-+ tid_tx->last_tx = jiffies;
- }
-
-- if (unlikely(!multicast && skb->sk &&
-+ if (unlikely(skb->sk &&
- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
- &info->flags, NULL);
-
-- if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
-- if (sdata->control_port_no_encrypt)
-- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-- info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
-- }
--
-- if (multicast)
-- info->flags |= IEEE80211_TX_CTL_NO_ACK;
--
- info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-
- ieee80211_tx_stats(dev, skb->len);
-
-- if (sta) {
-- sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-- sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-- }
-+ sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-+ sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
-@@ -4286,6 +4245,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
- struct net_device *dev)
- {
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-+ struct ethhdr *ehdr = (struct ethhdr *)skb->data;
- struct sta_info *sta;
-
- if (unlikely(skb->len < ETH_HLEN)) {
-@@ -4297,6 +4257,10 @@ netdev_tx_t ieee80211_subif_start_xmit_8
-
- if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
- kfree_skb(skb);
-+ else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
-+ !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
-+ sdata->control_port_protocol == ehdr->h_proto))
-+ ieee80211_subif_start_xmit(skb, dev);
- else
- ieee80211_8023_xmit(sdata, dev, sta, skb);
-
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 21 Aug 2020 05:51:58 +0200
+Subject: [PATCH] mac80211: notify the driver when a sta uses 4-address
+ mode
+
+This is needed for encapsulation offload of 4-address mode packets
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3840,6 +3840,8 @@ enum ieee80211_reconfig_type {
+ * This callback may sleep.
+ * @update_vif_config: Update virtual interface offload flags
+ * This callback may sleep.
++ * @sta_set_4addr: Called to notify the driver when a station starts/stops using
++ * 4-address mode
+ */
+ struct ieee80211_ops {
+ void (*tx)(struct ieee80211_hw *hw,
+@@ -4153,6 +4155,8 @@ struct ieee80211_ops {
+ struct ieee80211_sta *sta, u8 tids);
+ void (*update_vif_offload)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
++ void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, bool enabled);
+ };
+
+ /**
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1693,6 +1693,7 @@ static int ieee80211_change_station(stru
+
+ rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
+ __ieee80211_check_fast_rx_iface(vlansdata);
++ drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
+ }
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1400,4 +1400,18 @@ static inline void drv_update_vif_offloa
+ trace_drv_return_void(local);
+ }
+
++static inline void drv_sta_set_4addr(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_sta *sta, bool enabled)
++{
++ sdata = get_bss_sdata(sdata);
++ if (!check_sdata_in_driver(sdata))
++ return;
++
++ trace_drv_sta_set_4addr(local, sdata, sta, enabled);
++ if (local->ops->sta_set_4addr)
++ local->ops->sta_set_4addr(&local->hw, &sdata->vif, sta, enabled);
++ trace_drv_return_void(local);
++}
++
+ #endif /* __MAC80211_DRIVER_OPS */
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3518,6 +3518,9 @@ static bool ieee80211_assoc_success(stru
+ goto out;
+ }
+
++ if (sdata->wdev.use_4addr)
++ drv_sta_set_4addr(local, sdata, &sta->sta, true);
++
+ mutex_unlock(&sdata->local->sta_mtx);
+
+ /*
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -2739,6 +2739,33 @@ DEFINE_EVENT(local_sdata_addr_evt, drv_u
+ TP_ARGS(local, sdata)
+ );
+
++TRACE_EVENT(drv_sta_set_4addr,
++ TP_PROTO(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_sta *sta, bool enabled),
++
++ TP_ARGS(local, sdata, sta, enabled),
++
++ TP_STRUCT__entry(
++ LOCAL_ENTRY
++ VIF_ENTRY
++ STA_ENTRY
++ __field(bool, enabled)
++ ),
++
++ TP_fast_assign(
++ LOCAL_ASSIGN;
++ VIF_ASSIGN;
++ STA_ASSIGN;
++ __entry->enabled = enabled;
++ ),
++
++ TP_printk(
++ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " enabled:%d",
++ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->enabled
++ )
++);
++
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+
+ #undef TRACE_INCLUDE_PATH
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 21 Aug 2020 06:03:45 +0200
-Subject: [PATCH] mac80211: set info->control.hw_key for encap offload
- packets
-
-This is needed for drivers that don't do the key lookup themselves
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4185,6 +4185,7 @@ static void ieee80211_8023_xmit(struct i
- {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_key *key;
- struct tid_ampdu_tx *tid_tx;
- u8 tid;
-
-@@ -4233,6 +4234,10 @@ static void ieee80211_8023_xmit(struct i
- info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
- info->control.vif = &sdata->vif;
-
-+ key = rcu_dereference(sta->ptk[sta->ptk_idx]);
-+ if (key)
-+ info->control.hw_key = &key->conf;
-+
- ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
-
- return;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 27 Aug 2020 12:44:36 +0200
-Subject: [PATCH] mac80211: extend AQL aggregation estimation to HE and fix
- unit mismatch
-
-The unit of the return value of ieee80211_get_rate_duration is nanoseconds, not
-milliseconds. Adjust the duration checks to account for that.
-For higher data rates, allow larger estimated aggregation sizes, and add some
-values for HE as well, which can use much larger aggregates.
-Since small packets with high data rates can now lead to duration values too
-small for info->tx_time_est, return a minimum of 4us.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/airtime.c
-+++ b/net/mac80211/airtime.c
-@@ -668,20 +668,26 @@ u32 ieee80211_calc_expected_tx_airtime(s
- * This will not be very accurate, but much better than simply
- * assuming un-aggregated tx in all cases.
- */
-- if (duration > 400) /* <= VHT20 MCS2 1S */
-+ if (duration > 400 * 1024) /* <= VHT20 MCS2 1S */
- agg_shift = 1;
-- else if (duration > 250) /* <= VHT20 MCS3 1S or MCS1 2S */
-+ else if (duration > 250 * 1024) /* <= VHT20 MCS3 1S or MCS1 2S */
- agg_shift = 2;
-- else if (duration > 150) /* <= VHT20 MCS5 1S or MCS3 2S */
-+ else if (duration > 150 * 1024) /* <= VHT20 MCS5 1S or MCS2 2S */
- agg_shift = 3;
-- else
-+ else if (duration > 70 * 1024) /* <= VHT20 MCS5 2S */
- agg_shift = 4;
-+ else if (stat.encoding != RX_ENC_HE ||
-+ duration > 20 * 1024) /* <= HE40 MCS6 2S */
-+ agg_shift = 5;
-+ else
-+ agg_shift = 6;
-
- duration *= len;
- duration /= AVG_PKT_SIZE;
- duration /= 1024;
-+ duration += (overhead >> agg_shift);
-
-- return duration + (overhead >> agg_shift);
-+ return max_t(u32, duration, 4);
- }
-
- if (!conf)
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 8 Sep 2020 12:16:26 +0200
+Subject: [PATCH] mac80211: reorganize code to remove a forward
+ declaration
+
+Remove the newly added ieee80211_set_vif_encap_ops declaration.
+No further code changes
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -43,7 +43,6 @@
+ */
+
+ static void ieee80211_iface_work(struct work_struct *work);
+-static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata);
+
+ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ {
+@@ -349,6 +348,511 @@ static int ieee80211_check_queues(struct
+ return 0;
+ }
+
++static int ieee80211_open(struct net_device *dev)
++{
++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ int err;
++
++ /* fail early if user set an invalid address */
++ if (!is_valid_ether_addr(dev->dev_addr))
++ return -EADDRNOTAVAIL;
++
++ err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
++ if (err)
++ return err;
++
++ return ieee80211_do_open(&sdata->wdev, true);
++}
++
++static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
++ bool going_down)
++{
++ struct ieee80211_local *local = sdata->local;
++ unsigned long flags;
++ struct sk_buff *skb, *tmp;
++ u32 hw_reconf_flags = 0;
++ int i, flushed;
++ struct ps_data *ps;
++ struct cfg80211_chan_def chandef;
++ bool cancel_scan;
++ struct cfg80211_nan_func *func;
++
++ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
++
++ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
++ if (cancel_scan)
++ ieee80211_scan_cancel(local);
++
++ /*
++ * Stop TX on this interface first.
++ */
++ if (sdata->dev)
++ netif_tx_stop_all_queues(sdata->dev);
++
++ ieee80211_roc_purge(local, sdata);
++
++ switch (sdata->vif.type) {
++ case NL80211_IFTYPE_STATION:
++ ieee80211_mgd_stop(sdata);
++ break;
++ case NL80211_IFTYPE_ADHOC:
++ ieee80211_ibss_stop(sdata);
++ break;
++ case NL80211_IFTYPE_MONITOR:
++ if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
++ break;
++ list_del_rcu(&sdata->u.mntr.list);
++ break;
++ default:
++ break;
++ }
++
++ /*
++ * Remove all stations associated with this interface.
++ *
++ * This must be done before calling ops->remove_interface()
++ * because otherwise we can later invoke ops->sta_notify()
++ * whenever the STAs are removed, and that invalidates driver
++ * assumptions about always getting a vif pointer that is valid
++ * (because if we remove a STA after ops->remove_interface()
++ * the driver will have removed the vif info already!)
++ *
++ * In WDS mode a station must exist here and be flushed, for
++ * AP_VLANs stations may exist since there's nothing else that
++ * would have removed them, but in other modes there shouldn't
++ * be any stations.
++ */
++ flushed = sta_info_flush(sdata);
++ WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
++ ((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
++ (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)));
++
++ /* don't count this interface for allmulti while it is down */
++ if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
++ atomic_dec(&local->iff_allmultis);
++
++ if (sdata->vif.type == NL80211_IFTYPE_AP) {
++ local->fif_pspoll--;
++ local->fif_probe_req--;
++ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
++ local->fif_probe_req--;
++ }
++
++ if (sdata->dev) {
++ netif_addr_lock_bh(sdata->dev);
++ spin_lock_bh(&local->filter_lock);
++ __hw_addr_unsync(&local->mc_list, &sdata->dev->mc,
++ sdata->dev->addr_len);
++ spin_unlock_bh(&local->filter_lock);
++ netif_addr_unlock_bh(sdata->dev);
++ }
++
++ del_timer_sync(&local->dynamic_ps_timer);
++ cancel_work_sync(&local->dynamic_ps_enable_work);
++
++ cancel_work_sync(&sdata->recalc_smps);
++ sdata_lock(sdata);
++ mutex_lock(&local->mtx);
++ sdata->vif.csa_active = false;
++ if (sdata->vif.type == NL80211_IFTYPE_STATION)
++ sdata->u.mgd.csa_waiting_bcn = false;
++ if (sdata->csa_block_tx) {
++ ieee80211_wake_vif_queues(local, sdata,
++ IEEE80211_QUEUE_STOP_REASON_CSA);
++ sdata->csa_block_tx = false;
++ }
++ mutex_unlock(&local->mtx);
++ sdata_unlock(sdata);
++
++ cancel_work_sync(&sdata->csa_finalize_work);
++
++ cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
++
++ if (sdata->wdev.cac_started) {
++ chandef = sdata->vif.bss_conf.chandef;
++ WARN_ON(local->suspended);
++ mutex_lock(&local->mtx);
++ ieee80211_vif_release_channel(sdata);
++ mutex_unlock(&local->mtx);
++ cfg80211_cac_event(sdata->dev, &chandef,
++ NL80211_RADAR_CAC_ABORTED,
++ GFP_KERNEL);
++ }
++
++ /* APs need special treatment */
++ if (sdata->vif.type == NL80211_IFTYPE_AP) {
++ struct ieee80211_sub_if_data *vlan, *tmpsdata;
++
++ /* down all dependent devices, that is VLANs */
++ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
++ u.vlan.list)
++ dev_close(vlan->dev);
++ WARN_ON(!list_empty(&sdata->u.ap.vlans));
++ } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++ /* remove all packets in parent bc_buf pointing to this dev */
++ ps = &sdata->bss->ps;
++
++ spin_lock_irqsave(&ps->bc_buf.lock, flags);
++ skb_queue_walk_safe(&ps->bc_buf, skb, tmp) {
++ if (skb->dev == sdata->dev) {
++ __skb_unlink(skb, &ps->bc_buf);
++ local->total_ps_buffered--;
++ ieee80211_free_txskb(&local->hw, skb);
++ }
++ }
++ spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
++ }
++
++ if (going_down)
++ local->open_count--;
++
++ switch (sdata->vif.type) {
++ case NL80211_IFTYPE_AP_VLAN:
++ mutex_lock(&local->mtx);
++ list_del(&sdata->u.vlan.list);
++ mutex_unlock(&local->mtx);
++ RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
++ /* see comment in the default case below */
++ ieee80211_free_keys(sdata, true);
++ /* no need to tell driver */
++ break;
++ case NL80211_IFTYPE_MONITOR:
++ if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
++ local->cooked_mntrs--;
++ break;
++ }
++
++ local->monitors--;
++ if (local->monitors == 0) {
++ local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
++ hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
++ }
++
++ ieee80211_adjust_monitor_flags(sdata, -1);
++ break;
++ case NL80211_IFTYPE_NAN:
++ /* clean all the functions */
++ spin_lock_bh(&sdata->u.nan.func_lock);
++
++ idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, i) {
++ idr_remove(&sdata->u.nan.function_inst_ids, i);
++ cfg80211_free_nan_func(func);
++ }
++ idr_destroy(&sdata->u.nan.function_inst_ids);
++
++ spin_unlock_bh(&sdata->u.nan.func_lock);
++ break;
++ case NL80211_IFTYPE_P2P_DEVICE:
++ /* relies on synchronize_rcu() below */
++ RCU_INIT_POINTER(local->p2p_sdata, NULL);
++ /* fall through */
++ default:
++ cancel_work_sync(&sdata->work);
++ /*
++ * When we get here, the interface is marked down.
++ * Free the remaining keys, if there are any
++ * (which can happen in AP mode if userspace sets
++ * keys before the interface is operating, and maybe
++ * also in WDS mode)
++ *
++ * Force the key freeing to always synchronize_net()
++ * to wait for the RX path in case it is using this
++ * interface enqueuing frames at this very time on
++ * another CPU.
++ */
++ ieee80211_free_keys(sdata, true);
++ skb_queue_purge(&sdata->skb_queue);
++ }
++
++ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++ for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
++ skb_queue_walk_safe(&local->pending[i], skb, tmp) {
++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ if (info->control.vif == &sdata->vif) {
++ __skb_unlink(skb, &local->pending[i]);
++ ieee80211_free_txskb(&local->hw, skb);
++ }
++ }
++ }
++ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++
++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
++ ieee80211_txq_remove_vlan(local, sdata);
++
++ sdata->bss = NULL;
++
++ if (local->open_count == 0)
++ ieee80211_clear_tx_pending(local);
++
++ sdata->vif.bss_conf.beacon_int = 0;
++
++ /*
++ * If the interface goes down while suspended, presumably because
++ * the device was unplugged and that happens before our resume,
++ * then the driver is already unconfigured and the remainder of
++ * this function isn't needed.
++ * XXX: what about WoWLAN? If the device has software state, e.g.
++ * memory allocated, it might expect teardown commands from
++ * mac80211 here?
++ */
++ if (local->suspended) {
++ WARN_ON(local->wowlan);
++ WARN_ON(rtnl_dereference(local->monitor_sdata));
++ return;
++ }
++
++ switch (sdata->vif.type) {
++ case NL80211_IFTYPE_AP_VLAN:
++ break;
++ case NL80211_IFTYPE_MONITOR:
++ if (local->monitors == 0)
++ ieee80211_del_virtual_monitor(local);
++
++ mutex_lock(&local->mtx);
++ ieee80211_recalc_idle(local);
++ mutex_unlock(&local->mtx);
++
++ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
++ break;
++
++ /* fall through */
++ default:
++ if (going_down)
++ drv_remove_interface(local, sdata);
++ }
++
++ ieee80211_recalc_ps(local);
++
++ if (cancel_scan)
++ flush_delayed_work(&local->scan_work);
++
++ if (local->open_count == 0) {
++ ieee80211_stop_device(local);
++
++ /* no reconfiguring after stop! */
++ return;
++ }
++
++ /* do after stop to avoid reconfiguring when we stop anyway */
++ ieee80211_configure_filter(local);
++ ieee80211_hw_config(local, hw_reconf_flags);
++
++ if (local->monitors == local->open_count)
++ ieee80211_add_virtual_monitor(local);
++}
++
++static int ieee80211_stop(struct net_device *dev)
++{
++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++
++ ieee80211_do_stop(sdata, true);
++
++ return 0;
++}
++
++static void ieee80211_set_multicast_list(struct net_device *dev)
++{
++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ struct ieee80211_local *local = sdata->local;
++ int allmulti, sdata_allmulti;
++
++ allmulti = !!(dev->flags & IFF_ALLMULTI);
++ sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
++
++ if (allmulti != sdata_allmulti) {
++ if (dev->flags & IFF_ALLMULTI)
++ atomic_inc(&local->iff_allmultis);
++ else
++ atomic_dec(&local->iff_allmultis);
++ sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
++ }
++
++ spin_lock_bh(&local->filter_lock);
++ __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
++ spin_unlock_bh(&local->filter_lock);
++ ieee80211_queue_work(&local->hw, &local->reconfig_filter);
++}
++
++/*
++ * Called when the netdev is removed or, by the code below, before
++ * the interface type changes.
++ */
++static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
++{
++ int i;
++
++ /* free extra data */
++ ieee80211_free_keys(sdata, false);
++
++ ieee80211_debugfs_remove_netdev(sdata);
++
++ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
++ __skb_queue_purge(&sdata->fragments[i].skb_list);
++ sdata->fragment_next = 0;
++
++ if (ieee80211_vif_is_mesh(&sdata->vif))
++ ieee80211_mesh_teardown_sdata(sdata);
++}
++
++static void ieee80211_uninit(struct net_device *dev)
++{
++ ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
++}
++
++#if LINUX_VERSION_IS_GEQ(5,2,0)
++static u16 ieee80211_netdev_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ struct net_device *sb_dev)
++#elif LINUX_VERSION_IS_GEQ(4,19,0)
++static u16 ieee80211_netdev_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ struct net_device *sb_dev,
++ select_queue_fallback_t fallback)
++#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
++ (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
++static u16 ieee80211_netdev_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ void *accel_priv,
++ select_queue_fallback_t fallback)
++#elif LINUX_VERSION_IS_GEQ(3,13,0)
++static u16 ieee80211_netdev_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ void *accel_priv)
++#else
++static u16 ieee80211_netdev_select_queue(struct net_device *dev,
++ struct sk_buff *skb)
++#endif
++{
++ return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
++}
++
++static void
++ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
++{
++ int i;
++
++ for_each_possible_cpu(i) {
++ const struct pcpu_sw_netstats *tstats;
++ u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
++ unsigned int start;
++
++ tstats = per_cpu_ptr(netdev_tstats(dev), i);
++
++ do {
++ start = u64_stats_fetch_begin_irq(&tstats->syncp);
++ rx_packets = tstats->rx_packets;
++ tx_packets = tstats->tx_packets;
++ rx_bytes = tstats->rx_bytes;
++ tx_bytes = tstats->tx_bytes;
++ } while (u64_stats_fetch_retry_irq(&tstats->syncp, start));
++
++ stats->rx_packets += rx_packets;
++ stats->tx_packets += tx_packets;
++ stats->rx_bytes += rx_bytes;
++ stats->tx_bytes += tx_bytes;
++ }
++}
++#if LINUX_VERSION_IS_LESS(4,11,0)
++/* Just declare it here to keep sparse happy */
++struct rtnl_link_stats64 *bp_ieee80211_get_stats64(struct net_device *dev,
++ struct rtnl_link_stats64 *stats);
++struct rtnl_link_stats64 *
++bp_ieee80211_get_stats64(struct net_device *dev,
++ struct rtnl_link_stats64 *stats){
++ ieee80211_get_stats64(dev, stats);
++ return stats;
++}
++#endif
++
++static const struct net_device_ops ieee80211_dataif_ops = {
++ .ndo_open = ieee80211_open,
++ .ndo_stop = ieee80211_stop,
++ .ndo_uninit = ieee80211_uninit,
++ .ndo_start_xmit = ieee80211_subif_start_xmit,
++ .ndo_set_rx_mode = ieee80211_set_multicast_list,
++ .ndo_set_mac_address = ieee80211_change_mac,
++ .ndo_select_queue = ieee80211_netdev_select_queue,
++#if LINUX_VERSION_IS_GEQ(4,11,0)
++ .ndo_get_stats64 = ieee80211_get_stats64,
++#else
++ .ndo_get_stats64 = bp_ieee80211_get_stats64,
++#endif
++
++};
++
++#if LINUX_VERSION_IS_GEQ(5,2,0)
++static u16 ieee80211_monitor_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ struct net_device *sb_dev)
++#elif LINUX_VERSION_IS_GEQ(4,19,0)
++static u16 ieee80211_monitor_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ struct net_device *sb_dev,
++ select_queue_fallback_t fallback)
++#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
++ (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
++static u16 ieee80211_monitor_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ void *accel_priv,
++ select_queue_fallback_t fallback)
++#elif LINUX_VERSION_IS_GEQ(3,13,0)
++static u16 ieee80211_monitor_select_queue(struct net_device *dev,
++ struct sk_buff *skb,
++ void *accel_priv)
++#else
++static u16 ieee80211_monitor_select_queue(struct net_device *dev,
++ struct sk_buff *skb)
++#endif
++{
++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_hdr *hdr;
++ struct ieee80211_radiotap_header *rtap = (void *)skb->data;
++
++ if (local->hw.queues < IEEE80211_NUM_ACS)
++ return 0;
++
++ if (skb->len < 4 ||
++ skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
++ return 0; /* doesn't matter, frame will be dropped */
++
++ hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
++
++ return ieee80211_select_queue_80211(sdata, skb, hdr);
++}
++
++static const struct net_device_ops ieee80211_monitorif_ops = {
++ .ndo_open = ieee80211_open,
++ .ndo_stop = ieee80211_stop,
++ .ndo_uninit = ieee80211_uninit,
++ .ndo_start_xmit = ieee80211_monitor_start_xmit,
++ .ndo_set_rx_mode = ieee80211_set_multicast_list,
++ .ndo_set_mac_address = ieee80211_change_mac,
++ .ndo_select_queue = ieee80211_monitor_select_queue,
++#if LINUX_VERSION_IS_GEQ(4,11,0)
++ .ndo_get_stats64 = ieee80211_get_stats64,
++#else
++ .ndo_get_stats64 = bp_ieee80211_get_stats64,
++#endif
++
++};
++
++static const struct net_device_ops ieee80211_dataif_8023_ops = {
++ .ndo_open = ieee80211_open,
++ .ndo_stop = ieee80211_stop,
++ .ndo_uninit = ieee80211_uninit,
++ .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
++ .ndo_set_rx_mode = ieee80211_set_multicast_list,
++ .ndo_set_mac_address = ieee80211_change_mac,
++ .ndo_select_queue = ieee80211_netdev_select_queue,
++#if LINUX_VERSION_IS_GEQ(4,11,0)
++ .ndo_get_stats64 = ieee80211_get_stats64,
++#else
++ .ndo_get_stats64 = bp_ieee80211_get_stats64,
++#endif
++
++};
++
+ static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype)
+ {
+ switch (iftype) {
+@@ -389,6 +893,31 @@ static bool ieee80211_set_sdata_offload_
+ return true;
+ }
+
++static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_sub_if_data *bss = sdata;
++ bool enabled;
++
++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++ if (!sdata->bss)
++ return;
++
++ bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++ }
++
++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
++ !ieee80211_iftype_supports_encap_offload(bss->vif.type))
++ return;
++
++ enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
++ if (sdata->wdev.use_4addr &&
++ !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
++ enabled = false;
++
++ sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
++ &ieee80211_dataif_ops;
++}
+
+ static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata)
+ {
+@@ -866,511 +1395,6 @@ int ieee80211_do_open(struct wireless_de
+ return res;
+ }
+
+-static int ieee80211_open(struct net_device *dev)
+-{
+- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+- int err;
+-
+- /* fail early if user set an invalid address */
+- if (!is_valid_ether_addr(dev->dev_addr))
+- return -EADDRNOTAVAIL;
+-
+- err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
+- if (err)
+- return err;
+-
+- return ieee80211_do_open(&sdata->wdev, true);
+-}
+-
+-static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
+- bool going_down)
+-{
+- struct ieee80211_local *local = sdata->local;
+- unsigned long flags;
+- struct sk_buff *skb, *tmp;
+- u32 hw_reconf_flags = 0;
+- int i, flushed;
+- struct ps_data *ps;
+- struct cfg80211_chan_def chandef;
+- bool cancel_scan;
+- struct cfg80211_nan_func *func;
+-
+- clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+-
+- cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+- if (cancel_scan)
+- ieee80211_scan_cancel(local);
+-
+- /*
+- * Stop TX on this interface first.
+- */
+- if (sdata->dev)
+- netif_tx_stop_all_queues(sdata->dev);
+-
+- ieee80211_roc_purge(local, sdata);
+-
+- switch (sdata->vif.type) {
+- case NL80211_IFTYPE_STATION:
+- ieee80211_mgd_stop(sdata);
+- break;
+- case NL80211_IFTYPE_ADHOC:
+- ieee80211_ibss_stop(sdata);
+- break;
+- case NL80211_IFTYPE_MONITOR:
+- if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
+- break;
+- list_del_rcu(&sdata->u.mntr.list);
+- break;
+- default:
+- break;
+- }
+-
+- /*
+- * Remove all stations associated with this interface.
+- *
+- * This must be done before calling ops->remove_interface()
+- * because otherwise we can later invoke ops->sta_notify()
+- * whenever the STAs are removed, and that invalidates driver
+- * assumptions about always getting a vif pointer that is valid
+- * (because if we remove a STA after ops->remove_interface()
+- * the driver will have removed the vif info already!)
+- *
+- * In WDS mode a station must exist here and be flushed, for
+- * AP_VLANs stations may exist since there's nothing else that
+- * would have removed them, but in other modes there shouldn't
+- * be any stations.
+- */
+- flushed = sta_info_flush(sdata);
+- WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+- ((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
+- (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)));
+-
+- /* don't count this interface for allmulti while it is down */
+- if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+- atomic_dec(&local->iff_allmultis);
+-
+- if (sdata->vif.type == NL80211_IFTYPE_AP) {
+- local->fif_pspoll--;
+- local->fif_probe_req--;
+- } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+- local->fif_probe_req--;
+- }
+-
+- if (sdata->dev) {
+- netif_addr_lock_bh(sdata->dev);
+- spin_lock_bh(&local->filter_lock);
+- __hw_addr_unsync(&local->mc_list, &sdata->dev->mc,
+- sdata->dev->addr_len);
+- spin_unlock_bh(&local->filter_lock);
+- netif_addr_unlock_bh(sdata->dev);
+- }
+-
+- del_timer_sync(&local->dynamic_ps_timer);
+- cancel_work_sync(&local->dynamic_ps_enable_work);
+-
+- cancel_work_sync(&sdata->recalc_smps);
+- sdata_lock(sdata);
+- mutex_lock(&local->mtx);
+- sdata->vif.csa_active = false;
+- if (sdata->vif.type == NL80211_IFTYPE_STATION)
+- sdata->u.mgd.csa_waiting_bcn = false;
+- if (sdata->csa_block_tx) {
+- ieee80211_wake_vif_queues(local, sdata,
+- IEEE80211_QUEUE_STOP_REASON_CSA);
+- sdata->csa_block_tx = false;
+- }
+- mutex_unlock(&local->mtx);
+- sdata_unlock(sdata);
+-
+- cancel_work_sync(&sdata->csa_finalize_work);
+-
+- cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+-
+- if (sdata->wdev.cac_started) {
+- chandef = sdata->vif.bss_conf.chandef;
+- WARN_ON(local->suspended);
+- mutex_lock(&local->mtx);
+- ieee80211_vif_release_channel(sdata);
+- mutex_unlock(&local->mtx);
+- cfg80211_cac_event(sdata->dev, &chandef,
+- NL80211_RADAR_CAC_ABORTED,
+- GFP_KERNEL);
+- }
+-
+- /* APs need special treatment */
+- if (sdata->vif.type == NL80211_IFTYPE_AP) {
+- struct ieee80211_sub_if_data *vlan, *tmpsdata;
+-
+- /* down all dependent devices, that is VLANs */
+- list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
+- u.vlan.list)
+- dev_close(vlan->dev);
+- WARN_ON(!list_empty(&sdata->u.ap.vlans));
+- } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+- /* remove all packets in parent bc_buf pointing to this dev */
+- ps = &sdata->bss->ps;
+-
+- spin_lock_irqsave(&ps->bc_buf.lock, flags);
+- skb_queue_walk_safe(&ps->bc_buf, skb, tmp) {
+- if (skb->dev == sdata->dev) {
+- __skb_unlink(skb, &ps->bc_buf);
+- local->total_ps_buffered--;
+- ieee80211_free_txskb(&local->hw, skb);
+- }
+- }
+- spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
+- }
+-
+- if (going_down)
+- local->open_count--;
+-
+- switch (sdata->vif.type) {
+- case NL80211_IFTYPE_AP_VLAN:
+- mutex_lock(&local->mtx);
+- list_del(&sdata->u.vlan.list);
+- mutex_unlock(&local->mtx);
+- RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+- /* see comment in the default case below */
+- ieee80211_free_keys(sdata, true);
+- /* no need to tell driver */
+- break;
+- case NL80211_IFTYPE_MONITOR:
+- if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
+- local->cooked_mntrs--;
+- break;
+- }
+-
+- local->monitors--;
+- if (local->monitors == 0) {
+- local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
+- hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
+- }
+-
+- ieee80211_adjust_monitor_flags(sdata, -1);
+- break;
+- case NL80211_IFTYPE_NAN:
+- /* clean all the functions */
+- spin_lock_bh(&sdata->u.nan.func_lock);
+-
+- idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, i) {
+- idr_remove(&sdata->u.nan.function_inst_ids, i);
+- cfg80211_free_nan_func(func);
+- }
+- idr_destroy(&sdata->u.nan.function_inst_ids);
+-
+- spin_unlock_bh(&sdata->u.nan.func_lock);
+- break;
+- case NL80211_IFTYPE_P2P_DEVICE:
+- /* relies on synchronize_rcu() below */
+- RCU_INIT_POINTER(local->p2p_sdata, NULL);
+- /* fall through */
+- default:
+- cancel_work_sync(&sdata->work);
+- /*
+- * When we get here, the interface is marked down.
+- * Free the remaining keys, if there are any
+- * (which can happen in AP mode if userspace sets
+- * keys before the interface is operating, and maybe
+- * also in WDS mode)
+- *
+- * Force the key freeing to always synchronize_net()
+- * to wait for the RX path in case it is using this
+- * interface enqueuing frames at this very time on
+- * another CPU.
+- */
+- ieee80211_free_keys(sdata, true);
+- skb_queue_purge(&sdata->skb_queue);
+- }
+-
+- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+- for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
+- skb_queue_walk_safe(&local->pending[i], skb, tmp) {
+- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+- if (info->control.vif == &sdata->vif) {
+- __skb_unlink(skb, &local->pending[i]);
+- ieee80211_free_txskb(&local->hw, skb);
+- }
+- }
+- }
+- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+-
+- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+- ieee80211_txq_remove_vlan(local, sdata);
+-
+- sdata->bss = NULL;
+-
+- if (local->open_count == 0)
+- ieee80211_clear_tx_pending(local);
+-
+- sdata->vif.bss_conf.beacon_int = 0;
+-
+- /*
+- * If the interface goes down while suspended, presumably because
+- * the device was unplugged and that happens before our resume,
+- * then the driver is already unconfigured and the remainder of
+- * this function isn't needed.
+- * XXX: what about WoWLAN? If the device has software state, e.g.
+- * memory allocated, it might expect teardown commands from
+- * mac80211 here?
+- */
+- if (local->suspended) {
+- WARN_ON(local->wowlan);
+- WARN_ON(rtnl_dereference(local->monitor_sdata));
+- return;
+- }
+-
+- switch (sdata->vif.type) {
+- case NL80211_IFTYPE_AP_VLAN:
+- break;
+- case NL80211_IFTYPE_MONITOR:
+- if (local->monitors == 0)
+- ieee80211_del_virtual_monitor(local);
+-
+- mutex_lock(&local->mtx);
+- ieee80211_recalc_idle(local);
+- mutex_unlock(&local->mtx);
+-
+- if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
+- break;
+-
+- /* fall through */
+- default:
+- if (going_down)
+- drv_remove_interface(local, sdata);
+- }
+-
+- ieee80211_recalc_ps(local);
+-
+- if (cancel_scan)
+- flush_delayed_work(&local->scan_work);
+-
+- if (local->open_count == 0) {
+- ieee80211_stop_device(local);
+-
+- /* no reconfiguring after stop! */
+- return;
+- }
+-
+- /* do after stop to avoid reconfiguring when we stop anyway */
+- ieee80211_configure_filter(local);
+- ieee80211_hw_config(local, hw_reconf_flags);
+-
+- if (local->monitors == local->open_count)
+- ieee80211_add_virtual_monitor(local);
+-}
+-
+-static int ieee80211_stop(struct net_device *dev)
+-{
+- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+-
+- ieee80211_do_stop(sdata, true);
+-
+- return 0;
+-}
+-
+-static void ieee80211_set_multicast_list(struct net_device *dev)
+-{
+- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+- struct ieee80211_local *local = sdata->local;
+- int allmulti, sdata_allmulti;
+-
+- allmulti = !!(dev->flags & IFF_ALLMULTI);
+- sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
+-
+- if (allmulti != sdata_allmulti) {
+- if (dev->flags & IFF_ALLMULTI)
+- atomic_inc(&local->iff_allmultis);
+- else
+- atomic_dec(&local->iff_allmultis);
+- sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
+- }
+-
+- spin_lock_bh(&local->filter_lock);
+- __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
+- spin_unlock_bh(&local->filter_lock);
+- ieee80211_queue_work(&local->hw, &local->reconfig_filter);
+-}
+-
+-/*
+- * Called when the netdev is removed or, by the code below, before
+- * the interface type changes.
+- */
+-static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
+-{
+- int i;
+-
+- /* free extra data */
+- ieee80211_free_keys(sdata, false);
+-
+- ieee80211_debugfs_remove_netdev(sdata);
+-
+- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
+- __skb_queue_purge(&sdata->fragments[i].skb_list);
+- sdata->fragment_next = 0;
+-
+- if (ieee80211_vif_is_mesh(&sdata->vif))
+- ieee80211_mesh_teardown_sdata(sdata);
+-}
+-
+-static void ieee80211_uninit(struct net_device *dev)
+-{
+- ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
+-}
+-
+-#if LINUX_VERSION_IS_GEQ(5,2,0)
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- struct net_device *sb_dev)
+-#elif LINUX_VERSION_IS_GEQ(4,19,0)
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- struct net_device *sb_dev,
+- select_queue_fallback_t fallback)
+-#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
+- (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- void *accel_priv,
+- select_queue_fallback_t fallback)
+-#elif LINUX_VERSION_IS_GEQ(3,13,0)
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- void *accel_priv)
+-#else
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb)
+-#endif
+-{
+- return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
+-}
+-
+-static void
+-ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+-{
+- int i;
+-
+- for_each_possible_cpu(i) {
+- const struct pcpu_sw_netstats *tstats;
+- u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+- unsigned int start;
+-
+- tstats = per_cpu_ptr(netdev_tstats(dev), i);
+-
+- do {
+- start = u64_stats_fetch_begin_irq(&tstats->syncp);
+- rx_packets = tstats->rx_packets;
+- tx_packets = tstats->tx_packets;
+- rx_bytes = tstats->rx_bytes;
+- tx_bytes = tstats->tx_bytes;
+- } while (u64_stats_fetch_retry_irq(&tstats->syncp, start));
+-
+- stats->rx_packets += rx_packets;
+- stats->tx_packets += tx_packets;
+- stats->rx_bytes += rx_bytes;
+- stats->tx_bytes += tx_bytes;
+- }
+-}
+-#if LINUX_VERSION_IS_LESS(4,11,0)
+-/* Just declare it here to keep sparse happy */
+-struct rtnl_link_stats64 *bp_ieee80211_get_stats64(struct net_device *dev,
+- struct rtnl_link_stats64 *stats);
+-struct rtnl_link_stats64 *
+-bp_ieee80211_get_stats64(struct net_device *dev,
+- struct rtnl_link_stats64 *stats){
+- ieee80211_get_stats64(dev, stats);
+- return stats;
+-}
+-#endif
+-
+-static const struct net_device_ops ieee80211_dataif_ops = {
+- .ndo_open = ieee80211_open,
+- .ndo_stop = ieee80211_stop,
+- .ndo_uninit = ieee80211_uninit,
+- .ndo_start_xmit = ieee80211_subif_start_xmit,
+- .ndo_set_rx_mode = ieee80211_set_multicast_list,
+- .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_netdev_select_queue,
+-#if LINUX_VERSION_IS_GEQ(4,11,0)
+- .ndo_get_stats64 = ieee80211_get_stats64,
+-#else
+- .ndo_get_stats64 = bp_ieee80211_get_stats64,
+-#endif
+-
+-};
+-
+-#if LINUX_VERSION_IS_GEQ(5,2,0)
+-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- struct net_device *sb_dev)
+-#elif LINUX_VERSION_IS_GEQ(4,19,0)
+-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- struct net_device *sb_dev,
+- select_queue_fallback_t fallback)
+-#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
+- (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
+-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- void *accel_priv,
+- select_queue_fallback_t fallback)
+-#elif LINUX_VERSION_IS_GEQ(3,13,0)
+-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- void *accel_priv)
+-#else
+-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
+- struct sk_buff *skb)
+-#endif
+-{
+- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+- struct ieee80211_local *local = sdata->local;
+- struct ieee80211_hdr *hdr;
+- struct ieee80211_radiotap_header *rtap = (void *)skb->data;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS)
+- return 0;
+-
+- if (skb->len < 4 ||
+- skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
+- return 0; /* doesn't matter, frame will be dropped */
+-
+- hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
+-
+- return ieee80211_select_queue_80211(sdata, skb, hdr);
+-}
+-
+-static const struct net_device_ops ieee80211_monitorif_ops = {
+- .ndo_open = ieee80211_open,
+- .ndo_stop = ieee80211_stop,
+- .ndo_uninit = ieee80211_uninit,
+- .ndo_start_xmit = ieee80211_monitor_start_xmit,
+- .ndo_set_rx_mode = ieee80211_set_multicast_list,
+- .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_monitor_select_queue,
+-#if LINUX_VERSION_IS_GEQ(4,11,0)
+- .ndo_get_stats64 = ieee80211_get_stats64,
+-#else
+- .ndo_get_stats64 = bp_ieee80211_get_stats64,
+-#endif
+-
+-};
+-
+-static const struct net_device_ops ieee80211_dataif_8023_ops = {
+- .ndo_open = ieee80211_open,
+- .ndo_stop = ieee80211_stop,
+- .ndo_uninit = ieee80211_uninit,
+- .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
+- .ndo_set_rx_mode = ieee80211_set_multicast_list,
+- .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_netdev_select_queue,
+-#if LINUX_VERSION_IS_GEQ(4,11,0)
+- .ndo_get_stats64 = ieee80211_get_stats64,
+-#else
+- .ndo_get_stats64 = bp_ieee80211_get_stats64,
+-#endif
+-
+-};
+-
+ static void ieee80211_if_free(struct net_device *dev)
+ {
+ free_percpu(netdev_tstats(dev));
+@@ -1401,32 +1425,6 @@ static void ieee80211_if_setup_no_queue(
+ #endif
+ }
+
+-static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
+-{
+- struct ieee80211_local *local = sdata->local;
+- struct ieee80211_sub_if_data *bss = sdata;
+- bool enabled;
+-
+- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+- if (!sdata->bss)
+- return;
+-
+- bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
+- }
+-
+- if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
+- !ieee80211_iftype_supports_encap_offload(bss->vif.type))
+- return;
+-
+- enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
+- if (sdata->wdev.use_4addr &&
+- !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
+- enabled = false;
+-
+- sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
+- &ieee80211_dataif_ops;
+-}
+-
+ static void ieee80211_iface_work(struct work_struct *work)
+ {
+ struct ieee80211_sub_if_data *sdata =
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 27 Aug 2020 12:47:48 +0200
-Subject: [PATCH] mac80211: add AQL support for VHT160 tx rates
-
-When converting from struct ieee80211_tx_rate to ieee80211_rx_status,
-there was one check missing to fill in the bandwidth for 160 MHz
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/airtime.c
-+++ b/net/mac80211/airtime.c
-@@ -560,7 +560,9 @@ static int ieee80211_fill_rx_status(stru
- if (rate->idx < 0 || !rate->count)
- return -1;
-
-- if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
-+ if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
-+ stat->bw = RATE_INFO_BW_160;
-+ else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
- stat->bw = RATE_INFO_BW_80;
- else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- stat->bw = RATE_INFO_BW_40;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 27 Aug 2020 12:44:36 +0200
+Subject: [PATCH] mac80211: extend AQL aggregation estimation to HE and fix
+ unit mismatch
+
+The unit of the return value of ieee80211_get_rate_duration is nanoseconds, not
+milliseconds. Adjust the duration checks to account for that.
+For higher data rates, allow larger estimated aggregation sizes, and add some
+values for HE as well, which can use much larger aggregates.
+Since small packets with high data rates can now lead to duration values too
+small for info->tx_time_est, return a minimum of 4us.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/airtime.c
++++ b/net/mac80211/airtime.c
+@@ -668,20 +668,26 @@ u32 ieee80211_calc_expected_tx_airtime(s
+ * This will not be very accurate, but much better than simply
+ * assuming un-aggregated tx in all cases.
+ */
+- if (duration > 400) /* <= VHT20 MCS2 1S */
++ if (duration > 400 * 1024) /* <= VHT20 MCS2 1S */
+ agg_shift = 1;
+- else if (duration > 250) /* <= VHT20 MCS3 1S or MCS1 2S */
++ else if (duration > 250 * 1024) /* <= VHT20 MCS3 1S or MCS1 2S */
+ agg_shift = 2;
+- else if (duration > 150) /* <= VHT20 MCS5 1S or MCS3 2S */
++ else if (duration > 150 * 1024) /* <= VHT20 MCS5 1S or MCS2 2S */
+ agg_shift = 3;
+- else
++ else if (duration > 70 * 1024) /* <= VHT20 MCS5 2S */
+ agg_shift = 4;
++ else if (stat.encoding != RX_ENC_HE ||
++ duration > 20 * 1024) /* <= HE40 MCS6 2S */
++ agg_shift = 5;
++ else
++ agg_shift = 6;
+
+ duration *= len;
+ duration /= AVG_PKT_SIZE;
+ duration /= 1024;
++ duration += (overhead >> agg_shift);
+
+- return duration + (overhead >> agg_shift);
++ return max_t(u32, duration, 4);
+ }
+
+ if (!conf)
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 27 Aug 2020 12:47:48 +0200
+Subject: [PATCH] mac80211: add AQL support for VHT160 tx rates
+
+When converting from struct ieee80211_tx_rate to ieee80211_rx_status,
+there was one check missing to fill in the bandwidth for 160 MHz
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/airtime.c
++++ b/net/mac80211/airtime.c
+@@ -560,7 +560,9 @@ static int ieee80211_fill_rx_status(stru
+ if (rate->idx < 0 || !rate->count)
+ return -1;
+
+- if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
++ if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
++ stat->bw = RATE_INFO_BW_160;
++ else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ stat->bw = RATE_INFO_BW_80;
+ else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ stat->bw = RATE_INFO_BW_40;
const u8 *addr);
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
-@@ -1519,6 +1519,7 @@ enum ieee80211_smps_mode {
+@@ -1521,6 +1521,7 @@ enum ieee80211_smps_mode {
*
* @power_level: requested transmit power (in dBm), backward compatibility
* value only that is set to the minimum of all interfaces
*
* @chandef: the channel definition to tune to
* @radar_enabled: whether radar detection is enabled
-@@ -1539,6 +1540,7 @@ enum ieee80211_smps_mode {
+@@ -1541,6 +1542,7 @@ enum ieee80211_smps_mode {
struct ieee80211_conf {
u32 flags;
int power_level, dynamic_ps_timeout;
__NL80211_ATTR_AFTER_LAST,
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
-@@ -2615,6 +2615,19 @@ static int ieee80211_get_tx_power(struct
+@@ -2611,6 +2611,19 @@ static int ieee80211_get_tx_power(struct
return 0;
}
static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
const u8 *addr)
{
-@@ -4045,6 +4058,7 @@ const struct cfg80211_ops mac80211_confi
+@@ -4041,6 +4054,7 @@ const struct cfg80211_ops mac80211_confi
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,