aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch')
-rw-r--r--package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch651
1 files changed, 651 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch b/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch
new file mode 100644
index 0000000000..22697718f8
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch
@@ -0,0 +1,651 @@
+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
+@@ -4270,11 +4270,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;