Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 99B86C4360F for ; Tue, 2 Apr 2019 09:31:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 41C462082C for ; Tue, 2 Apr 2019 09:31:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=newmedia-net.de header.i=@newmedia-net.de header.b="tloBOgmc" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729162AbfDBJbu (ORCPT ); Tue, 2 Apr 2019 05:31:50 -0400 Received: from webmail.newmedia-net.de ([185.84.6.166]:37730 "EHLO webmail.newmedia-net.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726284AbfDBJbt (ORCPT ); Tue, 2 Apr 2019 05:31:49 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=newmedia-net.de; s=mikd; h=Content-Transfer-Encoding:Content-Type:In-Reply-To:MIME-Version:Date:Message-ID:From:References:Cc:To:Subject; bh=esfTtAe50GSV8i1fFIKsx6dmsLOolriYAOSm4MdtYlM=; b=tloBOgmc6SZfl2AHRatbUvwdf5biB42wYqMSBbLGr+H25biBym7LJSwqYfHKRdQrkWlmBBjWGeZsNu5H12vs1faGX1kVR6BhOb+L/3doJmTHa8hv/5sl38KXAgc1hT5+rklTHHJUyuqAaAlIHRTn5tEPub/vvt22HQtRAJ1hZH4=; Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support To: John Crispin , Johannes Berg , Kalle Valo Cc: linux-wireless@vger.kernel.org, Shashidhar Lakkavalli , Vasanthakumar Thiagarajan References: <20190401131416.22646-1-john@phrozen.org> <20190401131416.22646-2-john@phrozen.org> From: Sebastian Gottschall Message-ID: Date: Tue, 2 Apr 2019 11:31:34 +0200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Thunderbird/60.6.1 MIME-Version: 1.0 In-Reply-To: <20190401131416.22646-2-john@phrozen.org> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit X-Received: from [212.111.244.1] (helo=[172.29.0.186]) by webmail.newmedia-net.de with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.72) (envelope-from ) id 1hBFli-00052c-Vc; Tue, 02 Apr 2019 11:31:47 +0200 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org the introduced control field hw_80211_encap in ieee80211_tx_info leads to a compile assert /include/linux/compiler.h:522:38: error: call to '__compiletime_assert_1307' declared with attribute error: BUILD_BUG_ON failed: sizeof(struct ieee80211_tx_info) > sizeof(skb->cb) skb->cb is to small on x64 devices i suggest to implement hw_80211_encap in ieee80211_vif which is a member of control in ieee80211_tx_info Sebastian Am 01.04.2019 um 15:14 schrieb John Crispin: > From: Vasanthakumar Thiagarajan > > This patch adds a new transmit path for hardware that supports 802.11 > encapsulation offloading. In those cases 802.3a frames get passed > directly to the driver allowing to hardware to handle the encapsulation. > > Certain features wont work and the patch masks these out. > * monitor interfaces are not supported if any of the vif is in encap mode. > * amsdu/non-linear frames wont work in encap offloading mode. > * TKIP countermeasures cannot be triggered and hence those keys are not > accepted. > > The patch defines a secondary netdev_ops struct that the device is assigned > to the device if 802.11 encap support is available and enabled. The driver > needs to enable the support on a per vif basis if it finds that all > pre-reqs are meet. > > Signed-off-by: Vasanthakumar Thiagarajan > Signed-off-by: John Crispin > --- > include/net/mac80211.h | 25 ++++++ > net/mac80211/cfg.c | 12 ++- > net/mac80211/debugfs.c | 1 + > net/mac80211/ieee80211_i.h | 10 +++ > net/mac80211/iface.c | 54 +++++++++++++ > net/mac80211/key.c | 3 + > net/mac80211/main.c | 10 ++- > net/mac80211/status.c | 79 +++++++++++++++++++ > net/mac80211/tx.c | 188 +++++++++++++++++++++++++++++++++++++++++++-- > 9 files changed, 373 insertions(+), 9 deletions(-) > > diff --git a/include/net/mac80211.h b/include/net/mac80211.h > index ac2ed8ec662b..3e8929770839 100644 > --- a/include/net/mac80211.h > +++ b/include/net/mac80211.h > @@ -1021,6 +1021,7 @@ struct ieee80211_tx_info { > struct ieee80211_key_conf *hw_key; > u32 flags; > codel_time_t enqueue_time; > + u8 hw_80211_encap; > } control; > struct { > u64 cookie; > @@ -2243,6 +2244,9 @@ struct ieee80211_txq { > * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID > * only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set. > * > + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11 > + * encap for data frames. > + * > * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays > */ > enum ieee80211_hw_flags { > @@ -2294,6 +2298,7 @@ enum ieee80211_hw_flags { > IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN, > IEEE80211_HW_SUPPORTS_MULTI_BSSID, > IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID, > + IEEE80211_HW_SUPPORTS_80211_ENCAP, > > /* keep last, obviously */ > NUM_IEEE80211_HW_FLAGS > @@ -4587,6 +4592,25 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, > struct sk_buff *skb); > > /** > + * ieee80211_tx_status_8023 - transmit status callback for 802.3 frame format > + * > + * Call this function for all transmitted data frames after their transmit > + * completion. This callback should only be called for data frames which > + * are are using driver's (or hardware's) offload capability of encap/decap > + * 802.11 frames. > + * > + * This function may not be called in IRQ context. Calls to this function > + * for a single hardware must be synchronized against each other. > + * > + * @hw: the hardware the frame was transmitted by > + * @vif: the interface for which the frame was transmitted > + * @skb: the frame that was transmitted, owned by mac80211 after this call > + */ > +void ieee80211_tx_status_8023(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + struct sk_buff *skb); > + > +/** > * ieee80211_report_low_ack - report non-responding station > * > * When operating in AP-mode, call this function to report a non-responding > @@ -6359,4 +6383,5 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif, > struct cfg80211_nan_match_params *match, > gfp_t gfp); > > +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable); > #endif /* MAC80211_H */ > diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c > index 09dd1c2860fc..53b56f8fcdfc 100644 > --- a/net/mac80211/cfg.c > +++ b/net/mac80211/cfg.c > @@ -367,8 +367,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, > > /* reject WEP and TKIP keys if WEP failed to initialize */ > switch (params->cipher) { > - case WLAN_CIPHER_SUITE_WEP40: > case WLAN_CIPHER_SUITE_TKIP: > + /* countermeasures wont work on encap offload mode so reject > + * TKIP keys > + */ > + if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP)) > + return -EINVAL; > + > + /* drop through */ > + case WLAN_CIPHER_SUITE_WEP40: > case WLAN_CIPHER_SUITE_WEP104: > if (IS_ERR(local->wep_tx_tfm)) > return -EINVAL; > @@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) > if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { > ieee80211_check_fast_xmit_all(local); > > + if (ieee80211_is_hw_80211_encap(local)) > + return -EINVAL; > + > err = drv_set_frag_threshold(local, wiphy->frag_threshold); > > if (err) { > diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c > index 2d43bc127043..a4df6bca192f 100644 > --- a/net/mac80211/debugfs.c > +++ b/net/mac80211/debugfs.c > @@ -221,6 +221,7 @@ static const char *hw_flag_names[] = { > FLAG(TX_STATUS_NO_AMPDU_LEN), > FLAG(SUPPORTS_MULTI_BSSID), > FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID), > + FLAG(SUPPORTS_80211_ENCAP), > #undef FLAG > }; > > diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h > index e170f986d226..d7569a455a06 100644 > --- a/net/mac80211/ieee80211_i.h > +++ b/net/mac80211/ieee80211_i.h > @@ -987,6 +987,8 @@ struct ieee80211_sub_if_data { > } debugfs; > #endif > > + bool hw_80211_encap; > + > /* must be last, dynamically sized area in this! */ > struct ieee80211_vif vif; > }; > @@ -1732,6 +1734,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, > struct vif_params *params); > int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, > enum nl80211_iftype type); > +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local); > void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata); > void ieee80211_remove_interfaces(struct ieee80211_local *local); > u32 ieee80211_idle_off(struct ieee80211_local *local); > @@ -1759,6 +1762,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, > struct net_device *dev); > netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, > struct net_device *dev); > +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, > + struct net_device *dev); > void __ieee80211_subif_start_xmit(struct sk_buff *skb, > struct net_device *dev, > u32 info_flags); > @@ -1933,6 +1938,11 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, > struct sk_buff *skb, int tid, > enum nl80211_band band, u32 txdata_flags); > > +/* sta_out needs to be checked for ERR_PTR() before using */ > +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, > + struct sk_buff *skb, > + struct sta_info **sta_out); > + > static inline void > ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, > struct sk_buff *skb, int tid, > diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c > index 4a6ff1482a9f..4090c2c5fde0 100644 > --- a/net/mac80211/iface.c > +++ b/net/mac80211/iface.c > @@ -1177,6 +1177,59 @@ static const struct net_device_ops ieee80211_dataif_ops = { > .ndo_get_stats64 = ieee80211_get_stats64, > }; > > +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, > + .ndo_get_stats64 = ieee80211_get_stats64, > +}; > + > +void 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; > + > + if (!sdata->dev) > + return; > + > + if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP)) > + enable = 0; > + > + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) && > + (local->hw.wiphy->frag_threshold != (u32)-1)) > + enable = 0; > + > + if (enable) { > + sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops; > + sdata->hw_80211_encap = true; > + } else { > + sdata->dev->netdev_ops = &ieee80211_dataif_ops; > + sdata->hw_80211_encap = false; > + } > +} > +EXPORT_SYMBOL(ieee80211_set_hw_80211_encap); > + > +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local) > +{ > + struct ieee80211_sub_if_data *sdata; > + bool offloaded = false; > + > + mutex_lock(&local->iflist_mtx); > + list_for_each_entry(sdata, &local->interfaces, list) { > + if (sdata->hw_80211_encap) { > + offloaded = true; > + break; > + } > + } > + mutex_unlock(&local->iflist_mtx); > + > + return offloaded; > +} > + > static u16 ieee80211_monitor_select_queue(struct net_device *dev, > struct sk_buff *skb, > struct net_device *sb_dev, > @@ -1409,6 +1462,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, > sdata->vif.bss_conf.idle = true; > > sdata->noack_map = 0; > + sdata->hw_80211_encap = false; > > /* only monitor/p2p-device differ */ > if (sdata->dev) { > diff --git a/net/mac80211/key.c b/net/mac80211/key.c > index 4700718e010f..861b67d43eb7 100644 > --- a/net/mac80211/key.c > +++ b/net/mac80211/key.c > @@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) > key->conf.keyidx, > sta ? sta->sta.addr : bcast_addr, ret); > > + if (sdata->hw_80211_encap) > + return -EINVAL; > + > out_unsupported: > switch (key->conf.cipher) { > case WLAN_CIPHER_SUITE_WEP40: > diff --git a/net/mac80211/main.c b/net/mac80211/main.c > index 800e67615e2a..a49bcec3891e 100644 > --- a/net/mac80211/main.c > +++ b/net/mac80211/main.c > @@ -1000,9 +1000,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) > hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); > } > > - /* mac80211 always supports monitor */ > - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); > - hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); > + if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) { > + /* mac80211 always supports monitor unless we do 802.11 > + * encapsulation offloading. > + */ > + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); > + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); > + } > > /* mac80211 doesn't support more than one IBSS interface right now */ > for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { > diff --git a/net/mac80211/status.c b/net/mac80211/status.c > index 5b9952b1caf3..8feafaab88a4 100644 > --- a/net/mac80211/status.c > +++ b/net/mac80211/status.c > @@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw, > } > EXPORT_SYMBOL(ieee80211_tx_rate_update); > > +void ieee80211_tx_status_8023(struct ieee80211_hw *hw, > + 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 sta_info *sta; > + int retry_count; > + int rates_idx; > + bool acked; > + > + if (WARN_ON(!ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP))) > + goto skip_stats_update; > + > + 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)) { > + rcu_read_unlock(); > + goto counters_update; > + } > + > + if (!sta || IS_ERR(sta)) { > + rcu_read_unlock(); > + 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)) { > + 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) > + sta->status_stats.lost_packets = 0; > + > + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) > + sta->status_stats.last_tdls_pkt_time = jiffies; > + } else { > + ieee80211_lost_packet(sta, info); > + } > + } > + > + rcu_read_unlock(); > + > +counters_update: > + 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); > + > void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) > { > struct sta_info *sta = container_of(pubsta, struct sta_info, sta); > diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c > index 8a49a74c0a37..85356c208c02 100644 > --- a/net/mac80211/tx.c > +++ b/net/mac80211/tx.c > @@ -1253,7 +1253,8 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, > (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)) > return NULL; > > - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) { > + if (!info->control.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) || > vif->type == NL80211_IFTYPE_STATION) && > @@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local, > struct fq *fq = &local->fq; > struct fq_tin *tin = &txqi->tin; > > + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); > ieee80211_set_skb_enqueue_time(skb); > fq_tin_enqueue(fq, tin, skb, > fq_skb_free_func, > @@ -2357,9 +2359,9 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb) > skb->data[14] == WLAN_TDLS_SNAP_RFTYPE; > } > > -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, > - struct sk_buff *skb, > - struct sta_info **sta_out) > +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, > + struct sk_buff *skb, > + struct sta_info **sta_out) > { > struct sta_info *sta; > > @@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta) > struct ieee80211_chanctx_conf *chanctx_conf; > __le16 fc; > > - if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT)) > + /* check for driver support and ieee80211 encap offload */ > + if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) || > + sdata->hw_80211_encap) > return; > > /* Locking here protects both the pointer itself, and against concurrent > @@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, > hdr = (struct ieee80211_hdr *)skb->data; > info = IEEE80211_SKB_CB(skb); > > + if (info->control.hw_80211_encap) > + goto out; > + > memset(&tx, 0, sizeof(tx)); > __skb_queue_head_init(&tx.skbs); > tx.local = local; > @@ -4003,6 +4010,167 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, > return NETDEV_TX_OK; > } > > +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, > + struct sk_buff *skb, int led_len, > + struct sta_info *sta, > + bool txpending) > +{ > + struct ieee80211_local *local = sdata->local; > + struct ieee80211_tx_control control = {}; > + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); > + struct ieee80211_sta *pubsta = NULL; > + unsigned long flags; > + int q = info->hw_queue; > + > + if (ieee80211_queue_skb(local, sdata, sta, skb)) > + return true; > + > + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); > + > + if (local->queue_stop_reasons[q] || > + (!txpending && !skb_queue_empty(&local->pending[q]))) { > + if (txpending) > + skb_queue_head(&local->pending[q], skb); > + else > + skb_queue_tail(&local->pending[q], skb); > + > + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); > + > + return false; > + } > + > + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); > + > + if (sta && sta->uploaded) > + pubsta = &sta->sta; > + > + control.sta = pubsta; > + > + drv_tx(local, &control, skb); > + > + return true; > +} > + > +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_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; > + bool tdls_peer; > + unsigned char *ra = NULL; > + > + if (IS_ERR(sta) || (sta && !sta->uploaded)) > + sta = NULL; > + > + if (sdata->vif.type == NL80211_IFTYPE_STATION) { > + tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER); > + if (tdls_peer) > + ra = skb->data; > + else > + ra = sdata->u.mgd.bssid; > + } else { > + ra = ehdr->h_dest; > + } > + > + if (!ra) > + goto out_free; > + 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; > + > + /* TODO: Handle frames requiring wifi tx status to be notified */ > + > + memset(info, 0, sizeof(*info)); > + > + 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)]++; > + } > + > + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) > + sdata = container_of(sdata->bss, > + struct ieee80211_sub_if_data, u.ap); > + > + info->control.hw_80211_encap = true; > + info->control.vif = &sdata->vif; > + > + ieee80211_tx_8023(sdata, skb, skb->len, sta, false); > + > + return; > + > +out_free: > + kfree_skb(skb); > +} > + > +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, > + struct net_device *dev) > +{ > + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); > + struct sta_info *sta; > + > + if (WARN_ON(unlikely(!sdata->hw_80211_encap))) { > + kfree_skb(skb); > + return NETDEV_TX_OK; > + } > + > + if (unlikely(skb->len < ETH_HLEN)) { > + kfree_skb(skb); > + return NETDEV_TX_OK; > + } > + > + if (WARN_ON(dev->ieee80211_ptr->use_4addr)) { > + kfree_skb(skb); > + return NETDEV_TX_OK; > + } > + > + rcu_read_lock(); > + > + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) > + goto out_free; > + > + ieee80211_8023_xmit(sdata, dev, sta, skb); > + > + goto out; > + > +out_free: > + kfree_skb(skb); > +out: > + rcu_read_unlock(); > + > + return NETDEV_TX_OK; > +} > + > struct sk_buff * > ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, > struct sk_buff *skb, u32 info_flags) > @@ -4081,6 +4249,16 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, > } > info->band = chanctx_conf->def.chan->band; > result = ieee80211_tx(sdata, NULL, skb, true, 0); > + } else if (info->control.hw_80211_encap) { > + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { > + dev_kfree_skb(skb); > + return true; > + } > + > + if (IS_ERR(sta) || (sta && !sta->uploaded)) > + sta = NULL; > + > + result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true); > } else { > struct sk_buff_head skbs; >