From patchwork Tue Sep 8 12:36:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259318 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 8995EC433E2 for ; Tue, 8 Sep 2020 20:02:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4642C20658 for ; Tue, 8 Sep 2020 20:02:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="Y+y4JmY8" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732645AbgIHUC0 (ORCPT ); Tue, 8 Sep 2020 16:02:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50216 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730490AbgIHPek (ORCPT ); Tue, 8 Sep 2020 11:34:40 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 92CB9C061364 for ; Tue, 8 Sep 2020 05:37:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=s8LSK010ZEQutKKKguvk0l3oIK2Y3Zs3fmojxp/ExwI=; b=Y+y4JmY8PlXYel6C+sezgsqFYf N/rxXTlKig1KnyS4SnM6wHdhflQhl00TInbCXURYt0OlHFbc2W++ceWEdhSMcLs5W4JviKlkTrAVs g5nf5/VAMm9RThlH6IZqy+MrbTKRf3xfVqFyy5GPxYaKHrBR6jIDG4faJYNgON2eqY+g=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcrw-00050i-QW; Tue, 08 Sep 2020 14:37:04 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 02/14] mac80211: check and refresh aggregation session in encap offload tx Date: Tue, 8 Sep 2020 14:36:50 +0200 Message-Id: <20200908123702.88454-3-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org 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 --- net/mac80211/tx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index bee9b01c5a6b..cd54d197caab 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4198,6 +4198,8 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, 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; @@ -4235,6 +4237,22 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, 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, From patchwork Tue Sep 8 12:36:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259353 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 14C7EC433E2 for ; Tue, 8 Sep 2020 18:04:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AB4332074B for ; Tue, 8 Sep 2020 18:04:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="bAiVX73C" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732254AbgIHR6r (ORCPT ); Tue, 8 Sep 2020 13:58:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56688 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731554AbgIHQMq (ORCPT ); Tue, 8 Sep 2020 12:12:46 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E46E4C06136B for ; Tue, 8 Sep 2020 05:37:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=A9XAojkNO6R9JhxTA5mn7U4XymOAPDn6xmLAcEl7tqU=; b=bAiVX73C8WL+Oi2tBjhJDEfB17 mX0E9UXswyROTqCDuPr/i4KrTnPk3Nz5ZN5uzD9epVxhtSQBMqRw95LE4OjGwr5oKKd6jkj5blnzP B21J/9UQmAhMDJm0uOg60vXUdCApOeUnkFKLemF3v+cb9HqXNYMPBPatRjri0/unIy0E=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcrx-00050i-6u; Tue, 08 Sep 2020 14:37:05 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 04/14] mac80211: set info->control.hw_key for encap offload packets Date: Tue, 8 Sep 2020 14:36:52 +0200 Message-Id: <20200908123702.88454-5-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org This is needed for drivers that don't do the key lookup themselves Signed-off-by: Felix Fietkau --- net/mac80211/tx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ea750081f6c6..65dfc24916e8 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4194,6 +4194,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, { 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; @@ -4242,6 +4243,10 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, 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; From patchwork Tue Sep 8 12:36:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259320 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 1C353C2BC11 for ; Tue, 8 Sep 2020 19:37:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C140D21741 for ; Tue, 8 Sep 2020 19:37:32 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="Qj5S6aa5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731706AbgIHThQ (ORCPT ); Tue, 8 Sep 2020 15:37:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53426 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730980AbgIHP47 (ORCPT ); Tue, 8 Sep 2020 11:56:59 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A0E83C061366 for ; Tue, 8 Sep 2020 05:37:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=YpJ5b4/uraq5f4jo03aHFyeSgJXJ8Br4HO7rkRmQOJY=; b=Qj5S6aa5furv0sEl7Qizwqll6k UloeUQJ5mgIeDh6z5nDa5NK9yEKT6cO3l/+5O7fTdBjPEdBAQBBm2lXoVSIzUfw/0cVZ9SUmJIBc7 9BHV6k/aMExOPKyNpNvdTwoXVKJT7LNGXLsVgjHpbzPFQQcI9+FiWqZMDo4sVKy/WIvE=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcrx-00050i-D4; Tue, 08 Sep 2020 14:37:05 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 05/14] mac80211: rework tx encapsulation offload API Date: Tue, 8 Sep 2020 14:36:53 +0200 Message-Id: <20200908123702.88454-6-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org 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 --- drivers/net/wireless/ath/ath11k/mac.c | 57 +++++---- include/net/mac80211.h | 29 +++++ net/mac80211/debugfs.c | 1 + net/mac80211/driver-ops.h | 15 +++ net/mac80211/ieee80211_i.h | 3 +- net/mac80211/iface.c | 169 +++++++++++++++++--------- net/mac80211/key.c | 15 --- net/mac80211/trace.h | 6 + net/mac80211/tx.c | 34 +++--- 9 files changed, 217 insertions(+), 112 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 94ae2b9ea663..c100ac6298ce 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4304,6 +4304,35 @@ static int ath11k_set_he_mu_sounding_mode(struct ath11k *ar, 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) { @@ -4313,7 +4342,6 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, 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; @@ -4407,30 +4435,7 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, 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, @@ -5753,6 +5758,7 @@ static const struct ieee80211_ops ath11k_ops = { .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, @@ -6034,6 +6040,7 @@ static int __ath11k_mac_register(struct ath11k *ar) 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); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ec148b3e9c41..01612a82aacf 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1606,6 +1606,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 * @@ -1626,6 +1641,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 @@ -1662,6 +1682,7 @@ struct ieee80211_vif { struct ieee80211_chanctx_conf __rcu *chanctx_conf; u32 driver_flags; + u32 offload_flags; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *debugfs_dir; @@ -2328,6 +2349,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 { @@ -2380,6 +2404,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 @@ -3814,6 +3839,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, @@ -4125,6 +4152,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); }; /** diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 54080290d6e2..90470392fdaa 100644 --- 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 }; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 41d495d73d3a..e3dfb9307fae 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1384,4 +1384,19 @@ static inline int drv_reset_tid_config(struct ieee80211_local *local, 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 */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6bf879660a93..79e538700edc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -989,8 +989,6 @@ struct ieee80211_sub_if_data { } debugfs; #endif - bool hw_80211_encap; - /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; @@ -1767,6 +1765,7 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local); 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) { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 9740ae8fa697..02bb94de7bde 100644 --- 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 ieee80211_sub_if_data *sdata, 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_dev *wdev, bool coming_up) 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_dev *wdev, bool coming_up) 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_dev *wdev, bool coming_up) 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) @@ -1227,61 +1312,6 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = { .ndo_get_stats64 = ieee80211_get_stats64, }; -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(dev->tstats); @@ -1302,6 +1332,32 @@ static void ieee80211_if_setup_no_queue(struct net_device *dev) dev->priv_flags |= IFF_NO_QUEUE; } +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 = @@ -1484,7 +1540,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, 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) { @@ -1619,6 +1674,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, 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); @@ -1631,6 +1687,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, 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); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 2df636c32432..8c5f829ff6d7 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -177,13 +177,6 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) } } - /* 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(struct ieee80211_key *key) 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; - fallthrough; - case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_CMAC_256: case WLAN_CIPHER_SUITE_BIP_GMAC_128: diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 50ab5b9d8eab..50a0a83e96fc 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2734,6 +2734,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 diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 65dfc24916e8..1e829597f32c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4190,11 +4190,10 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, 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; @@ -4243,7 +4242,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, 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; @@ -4260,12 +4258,9 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, { 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); @@ -4274,15 +4269,26 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, 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; From patchwork Tue Sep 8 12:36:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259319 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 F2D8EC2D0A7 for ; Tue, 8 Sep 2020 20:00:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 936F320768 for ; Tue, 8 Sep 2020 20:00:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="J/kyG2pa" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730469AbgIHPff (ORCPT ); Tue, 8 Sep 2020 11:35:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50324 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729911AbgIHPek (ORCPT ); Tue, 8 Sep 2020 11:34:40 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 42924C061368 for ; Tue, 8 Sep 2020 05:37:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=kXaNdzZJduRfaOc+V4bsZYNRnkeBw/Drrl0jQEgix/0=; b=J/kyG2paoAWSk6r1v00NwLVvmq /JAHljzmi+z9+pIJIYhbdZylzVbGBRuONso/gqUDbWj5TYejKkk2epgyKhkdrXgiI0XgVV3P5i5lE 6jh0pUqd5xhgt1dlabvOF8yngcYwfFxZNYzlzYPC35MW7zLXAoMb8qwzkcBWzTz9L0Do=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcrx-00050i-Qf; Tue, 08 Sep 2020 14:37:05 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 07/14] mac80211: remove tx status call to ieee80211_sta_register_airtime Date: Tue, 8 Sep 2020 14:36:55 +0200 Message-Id: <20200908123702.88454-8-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org All drivers using airtime fairness are calling ieee80211_sta_register_airtime directly Signed-off-by: Felix Fietkau --- net/mac80211/status.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b3c356b3fc78..67df7a5acee5 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -988,12 +988,6 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, 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(). From patchwork Tue Sep 8 12:36:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259355 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 2AC3DC433E2 for ; Tue, 8 Sep 2020 16:43:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D43E8206E7 for ; Tue, 8 Sep 2020 16:43:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="YnMXpOyH" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732127AbgIHQmq (ORCPT ); Tue, 8 Sep 2020 12:42:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33162 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732019AbgIHQmS (ORCPT ); Tue, 8 Sep 2020 12:42:18 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5016C06137E for ; Tue, 8 Sep 2020 05:55:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=ZVeIXbED33B0oRI14KObhFDllCing8Gcc/5VA62luPM=; b=YnMXpOyHhFG5Rh3diGw5RK7l9Z flJagcBJTybVX05wJJYWlwBT808jTZdwoLJ7W6O5D+Z31ziihIZEFXrMtXs9JZcEx7B5qa7b3aJVW wMCzmRc8URInoS+BJeoMC/a3d9nBeDdaU5X1DasoUJ6eYFsNgJUUUKDobQ3atpFbq1ts=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcry-00050i-EX; Tue, 08 Sep 2020 14:37:06 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 10/14] mac80211: unify 802.3 (offload) and 802.11 tx status codepath Date: Tue, 8 Sep 2020 14:36:58 +0200 Message-Id: <20200908123702.88454-11-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Make ieee80211_tx_status_8023 call ieee80211_tx_status_ext, similar to ieee80211_tx_status. Signed-off-by: Felix Fietkau --- net/mac80211/status.c | 93 +++++++++++++------------------------------ 1 file changed, 28 insertions(+), 65 deletions(-) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index c873910d95e0..ac4d988556b0 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -894,7 +894,6 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, 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; @@ -987,17 +986,6 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, 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 @@ -1092,9 +1080,12 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = status->info; struct ieee80211_sta *pubsta = status->sta; + struct sk_buff *skb = status->skb; + struct sta_info *sta = NULL; struct ieee80211_supported_band *sband; int rates_idx, retry_count; bool acked, noack_success; + u16 tx_time_est; rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); @@ -1104,8 +1095,6 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED); if (pubsta) { - struct sta_info *sta; - sta = container_of(pubsta, struct sta_info, sta); if (!acked && !noack_success) @@ -1143,7 +1132,18 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, 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); @@ -1158,6 +1158,12 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, } 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); @@ -1184,66 +1190,23 @@ 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 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); From patchwork Tue Sep 8 12:37:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259356 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 153DFC43461 for ; Tue, 8 Sep 2020 16:42:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 99A9A20759 for ; Tue, 8 Sep 2020 16:42:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="Ml7etjZH" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732121AbgIHQmY (ORCPT ); Tue, 8 Sep 2020 12:42:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33168 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732055AbgIHQmS (ORCPT ); Tue, 8 Sep 2020 12:42:18 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BE681C0612EC for ; Tue, 8 Sep 2020 05:55:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=i/bTcAuFipdUZSCF5HstaUvxcCCN1hadYDv3oD7j7Sg=; b=Ml7etjZHy4HUui4+kwiJ0AZTyp O7WncQ/dtMX0f6YXr6iUoy+iTLNA0BJp6QSeLNmKZwtkndK5iaUOBSRwcMAXQZRTuXrHZ798pdXLd MPzZA2GRviPaVHIwbEEJNIh6SLIKLEftUEgy8Ii3LKVxXq/rtOfIWmlQDJHnXgKiLu2U=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcry-00050i-SG; Tue, 08 Sep 2020 14:37:06 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 12/14] mac80211: extend ieee80211_tx_status_ext to support bulk free Date: Tue, 8 Sep 2020 14:37:00 +0200 Message-Id: <20200908123702.88454-13-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Store processed skbs ready to be freed in a list so the driver bulk free them Signed-off-by: Felix Fietkau --- include/net/mac80211.h | 2 ++ net/mac80211/status.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index fc31976323e6..1d1290aca870 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1095,12 +1095,14 @@ ieee80211_info_get_tx_time_est(struct ieee80211_tx_info *info) * @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; }; /** diff --git a/net/mac80211/status.c b/net/mac80211/status.c index a3543c3dbd3d..a0dd926dceb3 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -1044,7 +1044,10 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, * 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; } @@ -1169,7 +1172,10 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, 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); From patchwork Tue Sep 8 12:37:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Fietkau X-Patchwork-Id: 259357 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=-12.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 A9438C43461 for ; Tue, 8 Sep 2020 16:37:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 36654206E7 for ; Tue, 8 Sep 2020 16:37:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=nbd.name header.i=@nbd.name header.b="WSd5ebio" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732070AbgIHQh0 (ORCPT ); Tue, 8 Sep 2020 12:37:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60568 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732034AbgIHQhO (ORCPT ); Tue, 8 Sep 2020 12:37:14 -0400 Received: from nbd.name (nbd.name [IPv6:2a01:4f8:221:3d45::2]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9FA4FC0612EE for ; Tue, 8 Sep 2020 05:55:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=nbd.name; s=20160729; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=2ulGxj0uRTY1f0oG2Ty3FWMRBbBxDkWKfyc6MCl+C0s=; b=WSd5ebioZTETdMenhJuCzcVUzQ eIGnRjPLtkds2vsIUbh/hMPKlVhORRGNllvtTp6cZrcvVq9KxyJ/9irDDNRIFW1p5yedQjVynJCL/ LkdWsrYbVk1pbAzVh10hTBFxd5Il27jfrGZui3IuWLOEHj6RyZ1eyHXgGE+PW2cx9UsY=; Received: from p4ff13fcb.dip0.t-ipconnect.de ([79.241.63.203] helo=localhost.localdomain) by ds12 with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.89) (envelope-from ) id 1kFcrz-00050i-AO; Tue, 08 Sep 2020 14:37:07 +0200 From: Felix Fietkau To: linux-wireless@vger.kernel.org Cc: johannes@sipsolutions.net Subject: [PATCH v4 14/14] mac80211: reorganize code to remove a forward declaration Date: Tue, 8 Sep 2020 14:37:02 +0200 Message-Id: <20200908123702.88454-15-nbd@nbd.name> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200908123702.88454-1-nbd@nbd.name> References: <20200908123702.88454-1-nbd@nbd.name> MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org Remove the newly added ieee80211_set_vif_encap_ops declaration. No further code changes Signed-off-by: Felix Fietkau --- net/mac80211/iface.c | 1626 +++++++++++++++++++++--------------------- 1 file changed, 812 insertions(+), 814 deletions(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 02bb94de7bde..7ac9af66f545 100644 --- 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,968 +348,993 @@ static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata, return 0; } -static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype) +static int ieee80211_open(struct net_device *dev) { - 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; - } + 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 bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdata) +static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, + bool going_down) { struct ieee80211_local *local = sdata->local; - u32 flags; - - flags = sdata->vif.offload_flags; + 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; - if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) && - ieee80211_iftype_supports_encap_offload(sdata->vif.type)) { - flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED; + clear_bit(SDATA_STATE_RUNNING, &sdata->state); - if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) && - local->hw.wiphy->frag_threshold != (u32)-1) - flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; + cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata; + if (cancel_scan) + ieee80211_scan_cancel(local); - if (local->monitors) - flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; - } else { - flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; - } + /* + * Stop TX on this interface first. + */ + if (sdata->dev) + netif_tx_stop_all_queues(sdata->dev); - if (sdata->vif.offload_flags == flags) - return false; + ieee80211_roc_purge(local, sdata); - sdata->vif.offload_flags = flags; - return true; -} + 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))); -static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_sub_if_data *vsdata; + /* don't count this interface for allmulti while it is down */ + if (sdata->flags & IEEE80211_SDATA_ALLMULTI) + atomic_dec(&local->iff_allmultis); - if (ieee80211_set_sdata_offload_flags(sdata)) { - drv_update_vif_offload(local, sdata); - ieee80211_set_vif_encap_ops(sdata); + 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--; } - 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); + 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); } -} - -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; + del_timer_sync(&local->dynamic_ps_timer); + cancel_work_sync(&local->dynamic_ps_enable_work); - ieee80211_recalc_sdata_offload(sdata); + 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); - mutex_unlock(&local->iflist_mtx); -} - -void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, - const int offset) -{ - struct ieee80211_local *local = sdata->local; - u32 flags = sdata->u.mntr.flags; + cancel_work_sync(&sdata->csa_finalize_work); -#define ADJUST(_f, _s) do { \ - if (flags & MONITOR_FLAG_##_f) \ - local->fif_##_s += offset; \ - } while (0) + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); - ADJUST(FCSFAIL, fcsfail); - ADJUST(PLCPFAIL, plcpfail); - ADJUST(CONTROL, control); - ADJUST(CONTROL, pspoll); - ADJUST(OTHER_BSS, other_bss); + 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); + } -#undef ADJUST -} + /* APs need special treatment */ + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct ieee80211_sub_if_data *vlan, *tmpsdata; -static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - int i; + /* 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; - for (i = 0; i < IEEE80211_NUM_ACS; i++) { - if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) - sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE; - else if (local->hw.queues >= IEEE80211_NUM_ACS) - sdata->vif.hw_queue[i] = i; - else - sdata->vif.hw_queue[i] = 0; + 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); } - sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; -} -int ieee80211_add_virtual_monitor(struct ieee80211_local *local) -{ - struct ieee80211_sub_if_data *sdata; - int ret; - - if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) - return 0; + if (going_down) + local->open_count--; - ASSERT_RTNL(); + 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; + } - if (local->monitor_sdata) - return 0; + local->monitors--; + if (local->monitors == 0) { + local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; + hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; + } - sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); - if (!sdata) - return -ENOMEM; + ieee80211_adjust_monitor_flags(sdata, -1); + break; + case NL80211_IFTYPE_NAN: + /* clean all the functions */ + spin_lock_bh(&sdata->u.nan.func_lock); - /* set up data */ - sdata->local = local; - sdata->vif.type = NL80211_IFTYPE_MONITOR; - snprintf(sdata->name, IFNAMSIZ, "%s-monitor", - wiphy_name(local->hw.wiphy)); - sdata->wdev.iftype = NL80211_IFTYPE_MONITOR; - - sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; - - ieee80211_set_default_queues(sdata); + 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); - ret = drv_add_interface(local, sdata); - if (WARN_ON(ret)) { - /* ok .. stupid driver, it asked for this! */ - kfree(sdata); - return ret; + 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); + fallthrough; + 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); } - ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR); - if (ret) { - kfree(sdata); - return ret; + 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); - mutex_lock(&local->iflist_mtx); - rcu_assign_pointer(local->monitor_sdata, sdata); - mutex_unlock(&local->iflist_mtx); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + ieee80211_txq_remove_vlan(local, sdata); - mutex_lock(&local->mtx); - ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, - IEEE80211_CHANCTX_EXCLUSIVE); - mutex_unlock(&local->mtx); - if (ret) { - mutex_lock(&local->iflist_mtx); - RCU_INIT_POINTER(local->monitor_sdata, NULL); - mutex_unlock(&local->iflist_mtx); - synchronize_net(); - drv_remove_interface(local, sdata); - kfree(sdata); - return ret; + 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; } - skb_queue_head_init(&sdata->skb_queue); - INIT_WORK(&sdata->work, ieee80211_iface_work); + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + break; + case NL80211_IFTYPE_MONITOR: + if (local->monitors == 0) + ieee80211_del_virtual_monitor(local); - return 0; -} + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); -void ieee80211_del_virtual_monitor(struct ieee80211_local *local) -{ - struct ieee80211_sub_if_data *sdata; + if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)) + break; - if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) - return; + fallthrough; + default: + if (going_down) + drv_remove_interface(local, sdata); + } - ASSERT_RTNL(); + ieee80211_recalc_ps(local); - mutex_lock(&local->iflist_mtx); + if (cancel_scan) + flush_delayed_work(&local->scan_work); - sdata = rcu_dereference_protected(local->monitor_sdata, - lockdep_is_held(&local->iflist_mtx)); - if (!sdata) { - mutex_unlock(&local->iflist_mtx); + if (local->open_count == 0) { + ieee80211_stop_device(local); + + /* no reconfiguring after stop! */ return; } - RCU_INIT_POINTER(local->monitor_sdata, NULL); - mutex_unlock(&local->iflist_mtx); + /* do after stop to avoid reconfiguring when we stop anyway */ + ieee80211_configure_filter(local); + ieee80211_hw_config(local, hw_reconf_flags); - synchronize_net(); + if (local->monitors == local->open_count) + ieee80211_add_virtual_monitor(local); +} - mutex_lock(&local->mtx); - ieee80211_vif_release_channel(sdata); - mutex_unlock(&local->mtx); +static int ieee80211_stop(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - drv_remove_interface(local, sdata); + ieee80211_do_stop(sdata, true); - kfree(sdata); + return 0; } -/* - * NOTE: Be very careful when changing this function, it must NOT return - * an error on interface type changes that have been pre-checked, so most - * checks should be in ieee80211_check_concurrent_iface. - */ -int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) +static void ieee80211_set_multicast_list(struct net_device *dev) { - struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - struct net_device *dev = wdev->netdev; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; - struct sta_info *sta; - u32 changed = 0; - int res; - u32 hw_reconf_flags = 0; + int allmulti, sdata_allmulti; - switch (sdata->vif.type) { - case NL80211_IFTYPE_WDS: - if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) - return -ENOLINK; - break; - case NL80211_IFTYPE_AP_VLAN: { - struct ieee80211_sub_if_data *master; + allmulti = !!(dev->flags & IFF_ALLMULTI); + sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI); - if (!sdata->bss) - return -ENOLINK; + 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; + } - mutex_lock(&local->mtx); - list_add(&sdata->u.vlan.list, &sdata->bss->vlans); - mutex_unlock(&local->mtx); + 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); +} - master = container_of(sdata->bss, - struct ieee80211_sub_if_data, u.ap); - sdata->control_port_protocol = - master->control_port_protocol; - sdata->control_port_no_encrypt = - master->control_port_no_encrypt; - sdata->control_port_over_nl80211 = - master->control_port_over_nl80211; - sdata->control_port_no_preauth = - master->control_port_no_preauth; - sdata->vif.cab_queue = master->vif.cab_queue; - memcpy(sdata->vif.hw_queue, master->vif.hw_queue, - sizeof(sdata->vif.hw_queue)); - sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef; +/* + * 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; - mutex_lock(&local->key_mtx); - sdata->crypto_tx_tailroom_needed_cnt += - master->crypto_tx_tailroom_needed_cnt; - mutex_unlock(&local->key_mtx); + /* free extra data */ + ieee80211_free_keys(sdata, false); - break; - } - case NL80211_IFTYPE_AP: - sdata->bss = &sdata->u.ap; - break; - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_P2P_DEVICE: - case NL80211_IFTYPE_OCB: - case NL80211_IFTYPE_NAN: - /* no special treatment */ - break; - case NL80211_IFTYPE_UNSPECIFIED: - case NUM_NL80211_IFTYPES: - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - /* cannot happen */ - WARN_ON(1); - break; - } + ieee80211_debugfs_remove_netdev(sdata); - if (local->open_count == 0) { - res = drv_start(local); - if (res) - goto err_del_bss; - /* we're brought up, everything changes */ - hw_reconf_flags = ~0; - ieee80211_led_radio(local, true); - ieee80211_mod_tpt_led_trig(local, - IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); - } + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) + __skb_queue_purge(&sdata->fragments[i].skb_list); + sdata->fragment_next = 0; - /* - * Copy the hopefully now-present MAC address to - * this interface, if it has the special null one. - */ - if (dev && is_zero_ether_addr(dev->dev_addr)) { - memcpy(dev->dev_addr, - local->hw.wiphy->perm_addr, - ETH_ALEN); - memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN); - - if (!is_valid_ether_addr(dev->dev_addr)) { - res = -EADDRNOTAVAIL; - goto err_stop; - } - } - - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP_VLAN: - /* no need to tell driver, but set carrier and chanctx */ - 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); - } - break; - case NL80211_IFTYPE_MONITOR: - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) { - local->cooked_mntrs++; - break; - } + if (ieee80211_vif_is_mesh(&sdata->vif)) + ieee80211_mesh_teardown_sdata(sdata); +} - if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) { - res = drv_add_interface(local, sdata); - if (res) - goto err_stop; - } else if (local->monitors == 0 && local->open_count == 0) { - res = ieee80211_add_virtual_monitor(local); - if (res) - goto err_stop; - } +static void ieee80211_uninit(struct net_device *dev) +{ + ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev)); +} - /* must be before the call to ieee80211_configure_filter */ - local->monitors++; - if (local->monitors == 1) { - local->hw.conf.flags |= IEEE80211_CONF_MONITOR; - hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; - } +static u16 ieee80211_netdev_select_queue(struct net_device *dev, + struct sk_buff *skb, + struct net_device *sb_dev) +{ + return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb); +} - 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); +static void +ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + int i; - netif_carrier_on(dev); - break; - default: - if (coming_up) { - ieee80211_del_virtual_monitor(local); - ieee80211_set_sdata_offload_flags(sdata); + for_each_possible_cpu(i) { + const struct pcpu_sw_netstats *tstats; + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + unsigned int start; - res = drv_add_interface(local, sdata); - if (res) - goto err_stop; + tstats = per_cpu_ptr(dev->tstats, i); - ieee80211_set_vif_encap_ops(sdata); - res = ieee80211_check_queues(sdata, - ieee80211_vif_type_p2p(&sdata->vif)); - if (res) - goto err_del_interface; - } + 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)); - if (sdata->vif.type == NL80211_IFTYPE_AP) { - local->fif_pspoll++; - local->fif_probe_req++; + stats->rx_packets += rx_packets; + stats->tx_packets += tx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_bytes += tx_bytes; + } +} - ieee80211_configure_filter(local); - } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { - local->fif_probe_req++; - } +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, + .ndo_get_stats64 = ieee80211_get_stats64, +}; - if (sdata->vif.probe_req_reg) - drv_config_iface_filter(local, sdata, - FIF_PROBE_REQ, - FIF_PROBE_REQ); +static u16 ieee80211_monitor_select_queue(struct net_device *dev, + struct sk_buff *skb, + struct net_device *sb_dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; + int len_rthdr; - if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && - sdata->vif.type != NL80211_IFTYPE_NAN) - changed |= ieee80211_reset_erp_info(sdata); - ieee80211_bss_info_change_notify(sdata, changed); + if (local->hw.queues < IEEE80211_NUM_ACS) + return 0; - switch (sdata->vif.type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_OCB: - netif_carrier_off(dev); - break; - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_P2P_DEVICE: - case NL80211_IFTYPE_NAN: - break; - default: - /* not reached */ - WARN_ON(1); - } + /* reset flags and info before parsing radiotap header */ + memset(info, 0, sizeof(*info)); - /* - * Set default queue parameters so drivers don't - * need to initialise the hardware if the hardware - * doesn't start up with sane defaults. - * Enable QoS for anything but station interfaces. - */ - ieee80211_set_wmm_default(sdata, true, - sdata->vif.type != NL80211_IFTYPE_STATION); - } + if (!ieee80211_parse_tx_radiotap(skb, dev)) + return 0; /* doesn't matter, frame will be dropped */ - set_bit(SDATA_STATE_RUNNING, &sdata->state); + len_rthdr = ieee80211_get_radiotap_len(skb->data); + hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); + if (skb->len < len_rthdr + 2 || + skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control)) + return 0; /* doesn't matter, frame will be dropped */ - switch (sdata->vif.type) { - case NL80211_IFTYPE_WDS: - /* Create STA entry for the WDS peer */ - sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr, - GFP_KERNEL); - if (!sta) { - res = -ENOMEM; - goto err_del_interface; - } + return ieee80211_select_queue_80211(sdata, skb, hdr); +} - sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); - sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); +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, + .ndo_get_stats64 = ieee80211_get_stats64, +}; - res = sta_info_insert(sta); - if (res) { - /* STA has been freed */ - goto err_del_interface; - } +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, +}; - rate_control_rate_init(sta); - netif_carrier_on(dev); - break; - case NL80211_IFTYPE_P2P_DEVICE: - rcu_assign_pointer(local->p2p_sdata, sdata); - break; - case NL80211_IFTYPE_MONITOR: - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) - break; - list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list); - break; +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: - break; + return false; } +} - /* - * set_multicast_list will be invoked by the networking core - * which will check whether any increments here were done in - * error and sync them down to the hardware as filter flags. - */ - if (sdata->flags & IEEE80211_SDATA_ALLMULTI) - atomic_inc(&local->iff_allmultis); - - if (coming_up) - local->open_count++; - - if (hw_reconf_flags) - ieee80211_hw_config(local, hw_reconf_flags); - - ieee80211_recalc_ps(local); +static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + u32 flags; - if (sdata->vif.type == NL80211_IFTYPE_MONITOR || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN || - local->ops->wake_tx_queue) { - /* XXX: for AP_VLAN, actually track AP queues */ - if (dev) - netif_tx_start_all_queues(dev); - } else if (dev) { - unsigned long flags; - int n_acs = IEEE80211_NUM_ACS; - int ac; + flags = sdata->vif.offload_flags; - if (local->hw.queues < IEEE80211_NUM_ACS) - n_acs = 1; + if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) && + ieee80211_iftype_supports_encap_offload(sdata->vif.type)) { + flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED; - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE || - (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 && - skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) { - for (ac = 0; ac < n_acs; ac++) { - int ac_queue = sdata->vif.hw_queue[ac]; + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) && + local->hw.wiphy->frag_threshold != (u32)-1) + flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; - if (local->queue_stop_reasons[ac_queue] == 0 && - skb_queue_empty(&local->pending[ac_queue])) - netif_start_subqueue(dev, ac); - } - } - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if (local->monitors) + flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; + } else { + flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; } - return 0; - err_del_interface: - drv_remove_interface(local, sdata); - err_stop: - if (!local->open_count) - drv_stop(local); - err_del_bss: - sdata->bss = NULL; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { - mutex_lock(&local->mtx); - list_del(&sdata->u.vlan.list); - mutex_unlock(&local->mtx); - } - /* might already be clear but that doesn't matter */ - clear_bit(SDATA_STATE_RUNNING, &sdata->state); - return res; + if (sdata->vif.offload_flags == flags) + return false; + + sdata->vif.offload_flags = flags; + return true; } -static int ieee80211_open(struct net_device *dev) +static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int err; + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *bss = sdata; + bool enabled; - /* fail early if user set an invalid address */ - if (!is_valid_ether_addr(dev->dev_addr)) - return -EADDRNOTAVAIL; + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + if (!sdata->bss) + return; - err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); - if (err) - return err; + bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + } - return ieee80211_do_open(&sdata->wdev, true); -} + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) || + !ieee80211_iftype_supports_encap_offload(bss->vif.type)) + return; -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; + enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED; + if (sdata->wdev.use_4addr && + !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR)) + enabled = false; - clear_bit(SDATA_STATE_RUNNING, &sdata->state); + sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops : + &ieee80211_dataif_ops; +} - cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata; - if (cancel_scan) - ieee80211_scan_cancel(local); +static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *vsdata; - /* - * Stop TX on this interface first. - */ - if (sdata->dev) - netif_tx_stop_all_queues(sdata->dev); + if (ieee80211_set_sdata_offload_flags(sdata)) { + drv_update_vif_offload(local, sdata); + ieee80211_set_vif_encap_ops(sdata); + } - ieee80211_roc_purge(local, sdata); + list_for_each_entry(vsdata, &local->interfaces, list) { + if (vsdata->vif.type != NL80211_IFTYPE_AP_VLAN || + vsdata->bss != &sdata->u.ap) + continue; - 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; + ieee80211_set_vif_encap_ops(vsdata); } +} - /* - * 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))); +void ieee80211_recalc_offload(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; - /* don't count this interface for allmulti while it is down */ - if (sdata->flags & IEEE80211_SDATA_ALLMULTI) - atomic_dec(&local->iff_allmultis); + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD)) + return; - 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--; - } + mutex_lock(&local->iflist_mtx); - 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); + list_for_each_entry(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + + ieee80211_recalc_sdata_offload(sdata); } - del_timer_sync(&local->dynamic_ps_timer); - cancel_work_sync(&local->dynamic_ps_enable_work); + mutex_unlock(&local->iflist_mtx); +} - 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); +void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, + const int offset) +{ + struct ieee80211_local *local = sdata->local; + u32 flags = sdata->u.mntr.flags; - cancel_work_sync(&sdata->csa_finalize_work); +#define ADJUST(_f, _s) do { \ + if (flags & MONITOR_FLAG_##_f) \ + local->fif_##_s += offset; \ + } while (0) - cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); + ADJUST(FCSFAIL, fcsfail); + ADJUST(PLCPFAIL, plcpfail); + ADJUST(CONTROL, control); + ADJUST(CONTROL, pspoll); + ADJUST(OTHER_BSS, other_bss); - 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); +#undef ADJUST +} + +static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + int i; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) + sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE; + else if (local->hw.queues >= IEEE80211_NUM_ACS) + sdata->vif.hw_queue[i] = i; + else + sdata->vif.hw_queue[i] = 0; } + sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; +} - /* APs need special treatment */ - if (sdata->vif.type == NL80211_IFTYPE_AP) { - struct ieee80211_sub_if_data *vlan, *tmpsdata; +int ieee80211_add_virtual_monitor(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + int ret; - /* 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; + if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) + return 0; - 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); - } + ASSERT_RTNL(); - if (going_down) - local->open_count--; + if (local->monitor_sdata) + return 0; - 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; - } + sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); + if (!sdata) + return -ENOMEM; - local->monitors--; - if (local->monitors == 0) { - local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR; - hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; - } + /* set up data */ + sdata->local = local; + sdata->vif.type = NL80211_IFTYPE_MONITOR; + snprintf(sdata->name, IFNAMSIZ, "%s-monitor", + wiphy_name(local->hw.wiphy)); + sdata->wdev.iftype = NL80211_IFTYPE_MONITOR; - ieee80211_adjust_monitor_flags(sdata, -1); - break; - case NL80211_IFTYPE_NAN: - /* clean all the functions */ - spin_lock_bh(&sdata->u.nan.func_lock); + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; - 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); + ieee80211_set_default_queues(sdata); - 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); - fallthrough; - 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); + ret = drv_add_interface(local, sdata); + if (WARN_ON(ret)) { + /* ok .. stupid driver, it asked for this! */ + kfree(sdata); + return ret; + } + + ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR); + if (ret) { + kfree(sdata); + return ret; } - 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); - } - } + mutex_lock(&local->iflist_mtx); + rcu_assign_pointer(local->monitor_sdata, sdata); + mutex_unlock(&local->iflist_mtx); + + mutex_lock(&local->mtx); + ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, + IEEE80211_CHANCTX_EXCLUSIVE); + mutex_unlock(&local->mtx); + if (ret) { + mutex_lock(&local->iflist_mtx); + RCU_INIT_POINTER(local->monitor_sdata, NULL); + mutex_unlock(&local->iflist_mtx); + synchronize_net(); + drv_remove_interface(local, sdata); + kfree(sdata); + return ret; } - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - ieee80211_txq_remove_vlan(local, sdata); + skb_queue_head_init(&sdata->skb_queue); + INIT_WORK(&sdata->work, ieee80211_iface_work); - sdata->bss = NULL; + return 0; +} - if (local->open_count == 0) - ieee80211_clear_tx_pending(local); +void ieee80211_del_virtual_monitor(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; - sdata->vif.bss_conf.beacon_int = 0; + if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) + return; - /* - * 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)); + ASSERT_RTNL(); + + mutex_lock(&local->iflist_mtx); + + sdata = rcu_dereference_protected(local->monitor_sdata, + lockdep_is_held(&local->iflist_mtx)); + if (!sdata) { + mutex_unlock(&local->iflist_mtx); return; } + RCU_INIT_POINTER(local->monitor_sdata, NULL); + mutex_unlock(&local->iflist_mtx); + + synchronize_net(); + + mutex_lock(&local->mtx); + ieee80211_vif_release_channel(sdata); + mutex_unlock(&local->mtx); + + drv_remove_interface(local, sdata); + + kfree(sdata); +} + +/* + * NOTE: Be very careful when changing this function, it must NOT return + * an error on interface type changes that have been pre-checked, so most + * checks should be in ieee80211_check_concurrent_iface. + */ +int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + struct net_device *dev = wdev->netdev; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + u32 changed = 0; + int res; + u32 hw_reconf_flags = 0; + switch (sdata->vif.type) { - case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) + return -ENOLINK; break; - case NL80211_IFTYPE_MONITOR: - if (local->monitors == 0) - ieee80211_del_virtual_monitor(local); + case NL80211_IFTYPE_AP_VLAN: { + struct ieee80211_sub_if_data *master; + + if (!sdata->bss) + return -ENOLINK; mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); + list_add(&sdata->u.vlan.list, &sdata->bss->vlans); mutex_unlock(&local->mtx); - if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)) - break; - - fallthrough; - default: - if (going_down) - drv_remove_interface(local, sdata); - } + master = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + sdata->control_port_protocol = + master->control_port_protocol; + sdata->control_port_no_encrypt = + master->control_port_no_encrypt; + sdata->control_port_over_nl80211 = + master->control_port_over_nl80211; + sdata->control_port_no_preauth = + master->control_port_no_preauth; + sdata->vif.cab_queue = master->vif.cab_queue; + memcpy(sdata->vif.hw_queue, master->vif.hw_queue, + sizeof(sdata->vif.hw_queue)); + sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef; - ieee80211_recalc_ps(local); + mutex_lock(&local->key_mtx); + sdata->crypto_tx_tailroom_needed_cnt += + master->crypto_tx_tailroom_needed_cnt; + mutex_unlock(&local->key_mtx); - if (cancel_scan) - flush_delayed_work(&local->scan_work); + break; + } + case NL80211_IFTYPE_AP: + sdata->bss = &sdata->u.ap; + break; + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_NAN: + /* no special treatment */ + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + /* cannot happen */ + WARN_ON(1); + break; + } if (local->open_count == 0) { - ieee80211_stop_device(local); - - /* no reconfiguring after stop! */ - return; + res = drv_start(local); + if (res) + goto err_del_bss; + /* we're brought up, everything changes */ + hw_reconf_flags = ~0; + ieee80211_led_radio(local, true); + ieee80211_mod_tpt_led_trig(local, + IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); } - /* 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); -} + /* + * Copy the hopefully now-present MAC address to + * this interface, if it has the special null one. + */ + if (dev && is_zero_ether_addr(dev->dev_addr)) { + memcpy(dev->dev_addr, + local->hw.wiphy->perm_addr, + ETH_ALEN); + memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN); -static int ieee80211_stop(struct net_device *dev) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (!is_valid_ether_addr(dev->dev_addr)) { + res = -EADDRNOTAVAIL; + goto err_stop; + } + } - ieee80211_do_stop(sdata, true); + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + /* no need to tell driver, but set carrier and chanctx */ + 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); + } + break; + case NL80211_IFTYPE_MONITOR: + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) { + local->cooked_mntrs++; + break; + } - return 0; -} + if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) { + res = drv_add_interface(local, sdata); + if (res) + goto err_stop; + } else if (local->monitors == 0 && local->open_count == 0) { + res = ieee80211_add_virtual_monitor(local); + if (res) + goto err_stop; + } -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; + /* must be before the call to ieee80211_configure_filter */ + local->monitors++; + if (local->monitors == 1) { + local->hw.conf.flags |= IEEE80211_CONF_MONITOR; + hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; + } - allmulti = !!(dev->flags & IFF_ALLMULTI); - sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI); + 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); - 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; - } + netif_carrier_on(dev); + break; + default: + if (coming_up) { + ieee80211_del_virtual_monitor(local); + ieee80211_set_sdata_offload_flags(sdata); - 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); -} + res = drv_add_interface(local, sdata); + if (res) + goto err_stop; -/* - * 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; + ieee80211_set_vif_encap_ops(sdata); + res = ieee80211_check_queues(sdata, + ieee80211_vif_type_p2p(&sdata->vif)); + if (res) + goto err_del_interface; + } - /* free extra data */ - ieee80211_free_keys(sdata, false); + if (sdata->vif.type == NL80211_IFTYPE_AP) { + local->fif_pspoll++; + local->fif_probe_req++; - ieee80211_debugfs_remove_netdev(sdata); + ieee80211_configure_filter(local); + } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + local->fif_probe_req++; + } - for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) - __skb_queue_purge(&sdata->fragments[i].skb_list); - sdata->fragment_next = 0; + if (sdata->vif.probe_req_reg) + drv_config_iface_filter(local, sdata, + FIF_PROBE_REQ, + FIF_PROBE_REQ); - if (ieee80211_vif_is_mesh(&sdata->vif)) - ieee80211_mesh_teardown_sdata(sdata); -} + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && + sdata->vif.type != NL80211_IFTYPE_NAN) + changed |= ieee80211_reset_erp_info(sdata); + ieee80211_bss_info_change_notify(sdata, changed); -static void ieee80211_uninit(struct net_device *dev) -{ - ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev)); -} + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: + netif_carrier_off(dev); + break; + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_NAN: + break; + default: + /* not reached */ + WARN_ON(1); + } -static u16 ieee80211_netdev_select_queue(struct net_device *dev, - struct sk_buff *skb, - struct net_device *sb_dev) -{ - return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb); -} + /* + * Set default queue parameters so drivers don't + * need to initialise the hardware if the hardware + * doesn't start up with sane defaults. + * Enable QoS for anything but station interfaces. + */ + ieee80211_set_wmm_default(sdata, true, + sdata->vif.type != NL80211_IFTYPE_STATION); + } -static void -ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) -{ - int i; + set_bit(SDATA_STATE_RUNNING, &sdata->state); - for_each_possible_cpu(i) { - const struct pcpu_sw_netstats *tstats; - u64 rx_packets, rx_bytes, tx_packets, tx_bytes; - unsigned int start; + switch (sdata->vif.type) { + case NL80211_IFTYPE_WDS: + /* Create STA entry for the WDS peer */ + sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr, + GFP_KERNEL); + if (!sta) { + res = -ENOMEM; + goto err_del_interface; + } - tstats = per_cpu_ptr(dev->tstats, i); + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); - 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)); + res = sta_info_insert(sta); + if (res) { + /* STA has been freed */ + goto err_del_interface; + } - stats->rx_packets += rx_packets; - stats->tx_packets += tx_packets; - stats->rx_bytes += rx_bytes; - stats->tx_bytes += tx_bytes; + rate_control_rate_init(sta); + netif_carrier_on(dev); + break; + case NL80211_IFTYPE_P2P_DEVICE: + rcu_assign_pointer(local->p2p_sdata, sdata); + break; + case NL80211_IFTYPE_MONITOR: + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) + break; + list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list); + break; + default: + break; } -} -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, - .ndo_get_stats64 = ieee80211_get_stats64, -}; + /* + * set_multicast_list will be invoked by the networking core + * which will check whether any increments here were done in + * error and sync them down to the hardware as filter flags. + */ + if (sdata->flags & IEEE80211_SDATA_ALLMULTI) + atomic_inc(&local->iff_allmultis); -static u16 ieee80211_monitor_select_queue(struct net_device *dev, - struct sk_buff *skb, - struct net_device *sb_dev) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr; - int len_rthdr; + if (coming_up) + local->open_count++; - if (local->hw.queues < IEEE80211_NUM_ACS) - return 0; + if (hw_reconf_flags) + ieee80211_hw_config(local, hw_reconf_flags); - /* reset flags and info before parsing radiotap header */ - memset(info, 0, sizeof(*info)); + ieee80211_recalc_ps(local); - if (!ieee80211_parse_tx_radiotap(skb, dev)) - return 0; /* doesn't matter, frame will be dropped */ + if (sdata->vif.type == NL80211_IFTYPE_MONITOR || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + local->ops->wake_tx_queue) { + /* XXX: for AP_VLAN, actually track AP queues */ + if (dev) + netif_tx_start_all_queues(dev); + } else if (dev) { + unsigned long flags; + int n_acs = IEEE80211_NUM_ACS; + int ac; - len_rthdr = ieee80211_get_radiotap_len(skb->data); - hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); - if (skb->len < len_rthdr + 2 || - skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control)) - return 0; /* doesn't matter, frame will be dropped */ + if (local->hw.queues < IEEE80211_NUM_ACS) + n_acs = 1; - return ieee80211_select_queue_80211(sdata, skb, hdr); -} + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE || + (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 && + skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) { + for (ac = 0; ac < n_acs; ac++) { + int ac_queue = sdata->vif.hw_queue[ac]; -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, - .ndo_get_stats64 = ieee80211_get_stats64, -}; + if (local->queue_stop_reasons[ac_queue] == 0 && + skb_queue_empty(&local->pending[ac_queue])) + netif_start_subqueue(dev, ac); + } + } + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } -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, -}; + return 0; + err_del_interface: + drv_remove_interface(local, sdata); + err_stop: + if (!local->open_count) + drv_stop(local); + err_del_bss: + sdata->bss = NULL; + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + mutex_lock(&local->mtx); + list_del(&sdata->u.vlan.list); + mutex_unlock(&local->mtx); + } + /* might already be clear but that doesn't matter */ + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + return res; +} static void ieee80211_if_free(struct net_device *dev) { @@ -1332,32 +1356,6 @@ static void ieee80211_if_setup_no_queue(struct net_device *dev) dev->priv_flags |= IFF_NO_QUEUE; } -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 =