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.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS,URIBL_BLOCKED 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 8C603C4360F for ; Tue, 2 Apr 2019 14:39:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3D2AE20882 for ; Tue, 2 Apr 2019 14:39:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730588AbfDBOj5 (ORCPT ); Tue, 2 Apr 2019 10:39:57 -0400 Received: from nbd.name ([46.4.11.11]:45412 "EHLO nbd.name" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729541AbfDBOj5 (ORCPT ); Tue, 2 Apr 2019 10:39:57 -0400 Received: from p548c86fc.dip0.t-ipconnect.de ([84.140.134.252] helo=[192.168.45.69]) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.89) (envelope-from ) id 1hBKZs-0003Uo-Up; Tue, 02 Apr 2019 16:39:53 +0200 Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support To: Sebastian Gottschall , 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: John Crispin Message-ID: <204cbbd7-fefc-52da-bd0f-c1302d609682@phrozen.org> Date: Tue, 2 Apr 2019 16:39:51 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.6.1 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Content-Language: en-US Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org On 02/04/2019 11:31, Sebastian Gottschall wrote: > 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 Hi Sebastian, stumbled across that aswell while testing on arm64 today. I'll fix it in V4, Thanks     John > > 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;