wifi: cfg80211/nl80211: Add support to indicate STA MLD setup links removal
authorVeerendranath Jakkam <quic_vjakkam@quicinc.com>
Fri, 17 Mar 2023 14:21:53 +0000 (19:51 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 19 Jun 2023 10:08:40 +0000 (12:08 +0200)
STA MLD setup links may get removed if AP MLD remove the corresponding
affiliated APs with Multi-Link reconfiguration as described in
P802.11be_D3.0, section 35.3.6.2.2 Removing affiliated APs. Currently,
there is no support to notify such operation to cfg80211 and userspace.

Add support for the drivers to indicate STA MLD setup links removal to
cfg80211 and notify the same to userspace. Upon receiving such
indication from the driver, clear the MLO links information of the
removed links in the WDEV.

Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
Link: https://lore.kernel.org/r/20230317142153.237900-1-quic_vjakkam@quicinc.com
[rename function and attribute, fix kernel-doc]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/sme.c
net/wireless/trace.h

index 9972de114d73f011e73385b965e55e353c7399c4..3a736f9286b002c4ca1458bb8b58959ed65e640f 100644 (file)
@@ -9205,4 +9205,17 @@ static inline int cfg80211_color_change_notify(struct net_device *dev)
 bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
                                              const struct cfg80211_chan_def *chandef);
 
+/**
+ * cfg80211_links_removed - Notify about removed STA MLD setup links.
+ * @dev: network device.
+ * @link_mask: BIT mask of removed STA MLD setup link IDs.
+ *
+ * Inform cfg80211 and the userspace about removed STA MLD setup links due to
+ * AP MLD removing the corresponding affiliated APs with Multi-Link
+ * reconfiguration. Note that it's not valid to remove all links, in this
+ * case disconnect instead.
+ * Also note that the wdev mutex must be held.
+ */
+void cfg80211_links_removed(struct net_device *dev, u16 link_mask);
+
 #endif /* __NET_CFG80211_H */
index 03939bdb0e48c35a6c11d86edf8de691934ee54b..3190d34269efb414174402fe7028b11591b4a945 100644 (file)
  *     The number of peers that HW timestamping can be enabled for concurrently
  *     is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
  *
+ * @NL80211_CMD_LINKS_REMOVED: Notify userspace about the removal of STA MLD
+ *     setup links due to AP MLD removing the corresponding affiliated APs with
+ *     Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide
+ *     information about the removed STA MLD setup links.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1562,6 +1567,8 @@ enum nl80211_commands {
 
        NL80211_CMD_SET_HW_TIMESTAMP,
 
+       NL80211_CMD_LINKS_REMOVED,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
index 291c6d83d56f77224356a9e6611ba643d9cba2dc..8a807b609ef73373bedb796030adab85072b4690 100644 (file)
@@ -576,5 +576,6 @@ void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id);
 void cfg80211_remove_links(struct wireless_dev *wdev);
 int cfg80211_remove_virtual_intf(struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev);
+void cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask);
 
 #endif /* __NET_WIRELESS_CORE_H */
index 7b547aeb52f1c016e1ed22993078c542c259c5d4..0da2e6a2a7ea947406b621ab5a88e29540607375 100644 (file)
@@ -18288,6 +18288,76 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
+void cfg80211_links_removed(struct net_device *dev, u16 link_mask)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct sk_buff *msg;
+       struct nlattr *links;
+       void *hdr;
+
+       ASSERT_WDEV_LOCK(wdev);
+       trace_cfg80211_links_removed(dev, link_mask);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
+               return;
+
+       if (WARN_ON(!wdev->valid_links || !link_mask ||
+                   (wdev->valid_links & link_mask) != link_mask ||
+                   wdev->valid_links == link_mask))
+               return;
+
+       cfg80211_wdev_release_link_bsses(wdev, link_mask);
+       wdev->valid_links &= ~link_mask;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_LINKS_REMOVED);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
+               goto nla_put_failure;
+
+       links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+       if (!links)
+               goto nla_put_failure;
+
+       while (link_mask) {
+               struct nlattr *link;
+               int link_id = __ffs(link_mask);
+
+               link = nla_nest_start(msg, link_id + 1);
+               if (!link)
+                       goto nla_put_failure;
+
+               if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+                       goto nla_put_failure;
+
+               nla_nest_end(msg, link);
+               link_mask &= ~(1 << link_id);
+       }
+
+       nla_nest_end(msg, links);
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, GFP_KERNEL);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_links_removed);
+
 void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, const u8 *bssid,
                             gfp_t gfp)
index 247369004aaab494a41b668b16894caa05203948..9bba233b5a6ec8a37f6bb7f85ffbe5c08a0829b1 100644 (file)
@@ -491,6 +491,21 @@ static void cfg80211_wdev_release_bsses(struct wireless_dev *wdev)
        }
 }
 
+void cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask)
+{
+       unsigned int link;
+
+       for_each_valid_link(wdev, link) {
+               if (!wdev->links[link].client.current_bss ||
+                   !(link_mask & BIT(link)))
+                       continue;
+               cfg80211_unhold_bss(wdev->links[link].client.current_bss);
+               cfg80211_put_bss(wdev->wiphy,
+                                &wdev->links[link].client.current_bss->pub);
+               wdev->links[link].client.current_bss = NULL;
+       }
+}
+
 static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
                                     const u8 *ies, size_t ies_len,
                                     const u8 **out_ies, size_t *out_ies_len)
index e63990b81249d831096de24860aee6b2a8d34f51..617c0d0dfa963c6b3fc2f937011a9b9a30cd5791 100644 (file)
@@ -3966,6 +3966,21 @@ TRACE_EVENT(rdev_set_hw_timestamp,
                  __entry->enable)
 );
 
+TRACE_EVENT(cfg80211_links_removed,
+       TP_PROTO(struct net_device *netdev, u16 link_mask),
+       TP_ARGS(netdev, link_mask),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __field(u16, link_mask)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               __entry->link_mask = link_mask;
+       ),
+       TP_printk(NETDEV_PR_FMT ", link_mask:%u", NETDEV_PR_ARG,
+                 __entry->link_mask)
+);
+
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH