]> git.baikalelectronics.ru Git - kernel.git/commitdiff
wifi: mac80211: optionally implement MLO multicast TX
authorJohannes Berg <johannes.berg@intel.com>
Thu, 21 Jul 2022 08:56:48 +0000 (10:56 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 22 Jul 2022 12:28:36 +0000 (14:28 +0200)
For drivers using software encryption for multicast TX, such
as mac80211_hwsim, mac80211 needs to duplicate the multicast
frames on each link, if MLO is enabled. Do this, but don't
just make it dependent on the key but provide a separate flag
for drivers to opt out of this.

This is not very efficient, I expect that drivers will do it
in firmware/hardware or at least with DMA engine assistence,
so this is mostly for hwsim.

To make this work, also implement the SNS11 sequence number
space that an AP MLD shall have, and modify the API to the
__ieee80211_subif_start_xmit() function to always require the
link ID bits to be set.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/debugfs.c
net/mac80211/ieee80211_i.h
net/mac80211/tdls.c
net/mac80211/tx.c

index 1afd45239fe6155f5903616fcfeb1d612ac9d82f..1e04961148bff4458ff27c28e25a1d9f71e7c300 100644 (file)
@@ -882,6 +882,8 @@ enum mac80211_tx_info_flags {
  * @IEEE80211_TX_CTRL_DONT_REORDER: This frame should not be reordered
  *     relative to other frames that have this flag set, independent
  *     of their QoS TID or other priority field values.
+ * @IEEE80211_TX_CTRL_MCAST_MLO_FIRST_TX: first MLO TX, used mostly internally
+ *     for sequence number assignment
  * @IEEE80211_TX_CTRL_MLO_LINK: If not @IEEE80211_LINK_UNSPECIFIED, this
  *     frame should be transmitted on the specific link. This really is
  *     only relevant for frames that do not have data present, and is
@@ -901,10 +903,14 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_INTCFL_NEED_TXPROCESSING   = BIT(6),
        IEEE80211_TX_CTRL_NO_SEQNO              = BIT(7),
        IEEE80211_TX_CTRL_DONT_REORDER          = BIT(8),
+       IEEE80211_TX_CTRL_MCAST_MLO_FIRST_TX    = BIT(9),
        IEEE80211_TX_CTRL_MLO_LINK              = 0xf0000000,
 };
 
 #define IEEE80211_LINK_UNSPECIFIED     0xf
+#define IEEE80211_TX_CTRL_MLO_LINK_UNSPEC      \
+       u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, \
+                       IEEE80211_TX_CTRL_MLO_LINK)
 
 /**
  * enum mac80211_tx_status_flags - flags to describe transmit status
@@ -2524,6 +2530,9 @@ struct ieee80211_txq {
  * @IEEE80211_HW_DETECTS_COLOR_COLLISION: HW/driver has support for BSS color
  *     collision detection and doesn't need it in software.
  *
+ * @IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX: Hardware/driver handles transmitting
+ *     multicast frames on all links, mac80211 should not do that.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2580,6 +2589,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
        IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
        IEEE80211_HW_DETECTS_COLOR_COLLISION,
+       IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
index 4d4341249759a9118d5f33278de3fe6db216754f..78c7d60e8667c5b0b67dad4665e78cd0621d911e 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
- * Copyright (C) 2018 - 2019, 2021 Intel Corporation
+ * Copyright (C) 2018 - 2019, 2021-2022 Intel Corporation
  */
 
 #include <linux/debugfs.h>
@@ -495,6 +495,7 @@ static const char *hw_flag_names[] = {
        FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
        FLAG(SUPPORTS_CONC_MON_RX_DECAP),
        FLAG(DETECTS_COLOR_COLLISION),
+       FLAG(MLO_MCAST_MULTI_LINK_TX),
 #undef FLAG
 };
 
index 4ec6fb96ba417e9ea77fc4ae93418959e199a5a2..f93b57799e940c84612e51f06da9e1ca1852b21d 100644 (file)
@@ -1029,6 +1029,7 @@ struct ieee80211_sub_if_data {
        struct ieee80211_key __rcu *default_unicast_key;
 
        u16 sequence_number;
+       u16 mld_mcast_seq;
        __be16 control_port_protocol;
        bool control_port_no_encrypt;
        bool control_port_no_preauth;
index 36bfc54b3d2d2470674d6cd9dc8dbe32921ad2da..f4b4d25eef95f1f47a9c4694ecc133c3b91267d9 100644 (file)
@@ -1054,7 +1054,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
 
        /* disable bottom halves when entering the Tx path */
        local_bh_disable();
-       __ieee80211_subif_start_xmit(skb, dev, flags, 0, NULL);
+       __ieee80211_subif_start_xmit(skb, dev, flags,
+                                    IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL);
        local_bh_enable();
 
        return ret;
index f24565064f08e5dc86a1318fb4ddcb20dcf18c2d..45df9932d0ba15bcb375e34cf2fe8c5a9abe5bef 100644 (file)
@@ -822,6 +822,16 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
        if (info->control.flags & IEEE80211_TX_CTRL_NO_SEQNO)
                return TX_CONTINUE;
 
+       /* SNS11 from 802.11be 10.3.2.14 */
+       if (unlikely(is_multicast_ether_addr(hdr->addr1) &&
+                    info->control.vif->valid_links &&
+                    info->control.vif->type == NL80211_IFTYPE_AP)) {
+               if (info->control.flags & IEEE80211_TX_CTRL_MCAST_MLO_FIRST_TX)
+                       tx->sdata->mld_mcast_seq += 0x10;
+               hdr->seq_ctrl = cpu_to_le16(tx->sdata->mld_mcast_seq);
+               return TX_CONTINUE;
+       }
+
        /*
         * Anything but QoS data that has a sequence number field
         * (is long enough) gets a sequence number from the global
@@ -2570,7 +2580,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_chanctx_conf *chanctx_conf = NULL;
        enum nl80211_band band;
        int ret;
-       u8 link_id = IEEE80211_LINK_UNSPECIFIED;
+       u8 link_id = u32_get_bits(ctrl_flags, IEEE80211_TX_CTRL_MLO_LINK);
 
        if (IS_ERR(sta))
                sta = NULL;
@@ -2630,8 +2640,18 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
                                goto free;
                        }
                        memcpy(hdr.addr2, link->conf->addr, ETH_ALEN);
-               } else {
+               } else if (link_id == IEEE80211_LINK_UNSPECIFIED) {
                        memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
+               } else {
+                       struct ieee80211_bss_conf *conf;
+
+                       conf = rcu_dereference(sdata->vif.link_conf[link_id]);
+                       if (unlikely(!conf)) {
+                               ret = -ENOLINK;
+                               goto free;
+                       }
+
+                       memcpy(hdr.addr2, conf->addr, ETH_ALEN);
                }
 
                memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
@@ -4222,9 +4242,6 @@ static bool ieee80211_multicast_to_unicast(struct sk_buff *skb,
        const struct vlan_ethhdr *ethvlan = (void *)skb->data;
        __be16 ethertype;
 
-       if (likely(!is_multicast_ether_addr(eth->h_dest)))
-               return false;
-
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
                if (sdata->u.vlan.sta)
@@ -4308,6 +4325,44 @@ out:
        rcu_read_unlock();
 }
 
+static void ieee80211_mlo_multicast_tx_one(struct ieee80211_sub_if_data *sdata,
+                                          struct sk_buff *skb, u32 ctrl_flags,
+                                          unsigned int link_id)
+{
+       struct sk_buff *out;
+
+       out = skb_copy(skb, GFP_ATOMIC);
+       if (!out)
+               return;
+
+       ctrl_flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK);
+       __ieee80211_subif_start_xmit(out, sdata->dev, 0, ctrl_flags, NULL);
+}
+
+static void ieee80211_mlo_multicast_tx(struct net_device *dev,
+                                      struct sk_buff *skb)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       unsigned long links = sdata->vif.valid_links;
+       unsigned int link;
+       u32 ctrl_flags = IEEE80211_TX_CTRL_MCAST_MLO_FIRST_TX;
+
+       if (hweight16(links) == 1) {
+               ctrl_flags |= u32_encode_bits(ffs(links) - 1,
+                                             IEEE80211_TX_CTRL_MLO_LINK);
+
+               __ieee80211_subif_start_xmit(skb, sdata->dev, 0, ctrl_flags,
+                                            NULL);
+               return;
+       }
+
+       for_each_set_bit(link, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+               ieee80211_mlo_multicast_tx_one(sdata, skb, ctrl_flags, link);
+               ctrl_flags = 0;
+       }
+       kfree_skb(skb);
+}
+
 /**
  * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
  * @skb: packet to be sent
@@ -4318,15 +4373,30 @@ out:
 netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                                       struct net_device *dev)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       const struct ethhdr *eth = (void *)skb->data;
+
+       if (likely(!is_multicast_ether_addr(eth->h_dest)))
+               goto normal;
+
        if (unlikely(ieee80211_multicast_to_unicast(skb, dev))) {
                struct sk_buff_head queue;
 
                __skb_queue_head_init(&queue);
                ieee80211_convert_to_unicast(skb, dev, &queue);
                while ((skb = __skb_dequeue(&queue)))
-                       __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
+                       __ieee80211_subif_start_xmit(skb, dev, 0,
+                                                    IEEE80211_TX_CTRL_MLO_LINK_UNSPEC,
+                                                    NULL);
+       } else if (sdata->vif.valid_links &&
+                  sdata->vif.type == NL80211_IFTYPE_AP &&
+                  !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX)) {
+               ieee80211_mlo_multicast_tx(dev, skb);
        } else {
-               __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
+normal:
+               __ieee80211_subif_start_xmit(skb, dev, 0,
+                                            IEEE80211_TX_CTRL_MLO_LINK_UNSPEC,
+                                            NULL);
        }
 
        return NETDEV_TX_OK;
@@ -4410,7 +4480,9 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
        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);
+                       __ieee80211_subif_start_xmit(skb, dev, 0,
+                                                    IEEE80211_TX_CTRL_MLO_LINK_UNSPEC,
+                                                    NULL);
                        return;
                }
 
@@ -4512,7 +4584,8 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       skb = ieee80211_build_hdr(sdata, skb, info_flags, sta, 0, NULL);
+       skb = ieee80211_build_hdr(sdata, skb, info_flags, sta,
+                                 IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL);
        if (IS_ERR(skb))
                goto out;