]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: cfg80211: Add support for dynamic addition/removal of links
authorIlan Peer <ilan.peer@intel.com>
Thu, 2 Jan 2025 14:19:55 +0000 (16:19 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 13 Jan 2025 14:34:08 +0000 (15:34 +0100)
Add support for requesting dynamic addition/removal of links to the
current MLO association.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250102161730.cef23352f2a2.I79c849974c494cb1cbf9e1b22a5d2d37395ff5ac@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/rdev-ops.h
net/wireless/trace.h

index 8a38d66be2c962fcb53d217ed45c29ed400527ae..d4b4cabac029feac9d712de5160f7a616876b8ac 100644 (file)
@@ -4596,6 +4596,15 @@ struct mgmt_frame_regs {
  * @set_ttlm: set the TID to link mapping.
  * @get_radio_mask: get bitmask of radios in use.
  *     (invoked with the wiphy mutex held)
+ * @assoc_ml_reconf: Request a non-AP MLO connection to perform ML
+ *     reconfiguration, i.e., add and/or remove links to/from the
+ *     association using ML reconfiguration action frames. Successfully added
+ *     links will be added to the set of valid links. Successfully removed
+ *     links will be removed from the set of valid links. The driver must
+ *     indicate removed links by calling cfg80211_links_removed() and added
+ *     links by calling cfg80211_mlo_reconf_add_done(). When calling
+ *     cfg80211_mlo_reconf_add_done() the bss pointer must be given for each
+ *     link for which MLO reconfiguration 'add' operation was requested.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -4959,6 +4968,9 @@ struct cfg80211_ops {
        int     (*set_ttlm)(struct wiphy *wiphy, struct net_device *dev,
                            struct cfg80211_ttlm_params *params);
        u32     (*get_radio_mask)(struct wiphy *wiphy, struct net_device *dev);
+       int     (*assoc_ml_reconf)(struct wiphy *wiphy, struct net_device *dev,
+                                  struct cfg80211_assoc_link *add_links,
+                                  u16 rem_links);
 };
 
 /*
@@ -9716,6 +9728,39 @@ static inline int cfg80211_color_change_notify(struct net_device *dev,
  */
 void cfg80211_links_removed(struct net_device *dev, u16 link_mask);
 
+/**
+ * struct cfg80211_mlo_reconf_done_data - MLO reconfiguration data
+ * @buf: MLO Reconfiguration Response frame (header + body)
+ * @len: length of the frame data
+ * @added_links: BIT mask of links successfully added to the association
+ * @links: per-link information indexed by link ID
+ * @links.bss: the BSS that MLO reconfiguration was requested for, ownership of
+ *      the pointer moves to cfg80211 in the call to
+ *      cfg80211_mlo_reconf_add_done().
+ *
+ * The BSS pointer must be set for each link for which 'add' operation was
+ * requested in the assoc_ml_reconf callback.
+ */
+struct cfg80211_mlo_reconf_done_data {
+       const u8 *buf;
+       size_t len;
+       u16 added_links;
+       struct {
+               struct cfg80211_bss *bss;
+       } links[IEEE80211_MLD_MAX_NUM_LINKS];
+};
+
+/**
+ * cfg80211_mlo_reconf_add_done - Notify about MLO reconfiguration result
+ * @dev: network device.
+ * @data: MLO reconfiguration done data, &struct cfg80211_mlo_reconf_done_data
+ *
+ * Inform cfg80211 and the userspace that processing of ML reconfiguration
+ * request to add links to the association is done.
+ */
+void cfg80211_mlo_reconf_add_done(struct net_device *dev,
+                                 struct cfg80211_mlo_reconf_done_data *data);
+
 /**
  * cfg80211_schedule_channels_check - schedule regulatory check if needed
  * @wdev: the wireless device to check
index ee1cf19803ea77068fe78a824e0896b484b0cd85..f900d7cc42bceb2ff156fdc7d834130e4572c9fa 100644 (file)
  *      %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the
  *      TID to Link mapping for downlink/uplink traffic.
  *
+ * @NL80211_CMD_ASSOC_MLO_RECONF: For a non-AP MLD station, request to
+ *      add/remove links to/from the association.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1586,6 +1589,8 @@ enum nl80211_commands {
 
        NL80211_CMD_SET_TID_TO_LINK_MAPPING,
 
+       NL80211_CMD_ASSOC_MLO_RECONF,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -2877,6 +2882,9 @@ enum nl80211_commands {
  *     This can be used to provide a list of selectors that are implemented
  *     by the supplicant. If not given, support for SAE_H2E is assumed.
  *
+ * @NL80211_ATTR_MLO_RECONF_REM_LINKS: (u16) A bitmask of the links requested
+ *      to be removed from the MLO association.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3429,6 +3437,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_SUPPORTED_SELECTORS,
 
+       NL80211_ATTR_MLO_RECONF_REM_LINKS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 4c45f994a8c0c949ea9cf15a8dfa637753f5f75d..826299f3d78132f9cb12749b9316059cd3ab0121 100644 (file)
@@ -567,6 +567,10 @@ 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);
 
+int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev,
+                            struct net_device *dev,
+                            struct cfg80211_assoc_link *links,
+                            u16 rem_links);
 /**
  * struct cfg80211_colocated_ap - colocated AP information
  *
index 5c09bf4cdc2e36e38b4be59d8f985973f05f53af..e10f2b3b4b7f64d3512dea76c9a3e6917352d5f1 100644 (file)
@@ -1294,3 +1294,80 @@ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
                                        &rdev->background_radar_chandef,
                                        NL80211_RADAR_CAC_ABORTED);
 }
+
+int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev,
+                            struct net_device *dev,
+                            struct cfg80211_assoc_link *links,
+                            u16 rem_links)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       int err;
+
+       lockdep_assert_wiphy(wdev->wiphy);
+
+       err = rdev_assoc_ml_reconf(rdev, dev, links, rem_links);
+       if (!err) {
+               int link_id;
+
+               for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
+                    link_id++) {
+                       if (!links[link_id].bss)
+                               continue;
+
+                       cfg80211_ref_bss(&rdev->wiphy, links[link_id].bss);
+                       cfg80211_hold_bss(bss_from_pub(links[link_id].bss));
+               }
+       }
+
+       return err;
+}
+
+void cfg80211_mlo_reconf_add_done(struct net_device *dev,
+                                 struct cfg80211_mlo_reconf_done_data *data)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       int link_id;
+
+       lockdep_assert_wiphy(wiphy);
+
+       trace_cfg80211_mlo_reconf_add_done(dev, data->added_links,
+                                          data->buf, data->len);
+
+       if (WARN_ON(!wdev->valid_links))
+               return;
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
+               return;
+
+       /* validate that a BSS is given for each added link */
+       for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
+               struct cfg80211_bss *bss = data->links[link_id].bss;
+
+               if (!(data->added_links & BIT(link_id)))
+                       continue;
+
+               if (WARN_ON(!bss))
+                       return;
+       }
+
+       for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
+               struct cfg80211_bss *bss = data->links[link_id].bss;
+
+               if (!bss)
+                       continue;
+
+               if (data->added_links & BIT(link_id)) {
+                       wdev->links[link_id].client.current_bss =
+                               bss_from_pub(bss);
+               } else {
+                       cfg80211_unhold_bss(bss_from_pub(bss));
+                       cfg80211_put_bss(wiphy, bss);
+               }
+       }
+
+       wdev->valid_links |= data->added_links;
+       nl80211_mlo_reconf_add_done(dev, data);
+}
+EXPORT_SYMBOL(cfg80211_mlo_reconf_add_done);
index 5bf9a27e2c68e05043b613735b20f2db11d4a549..199fa3da31e9653edf564bf9d4e6d056cea27dd2 100644 (file)
@@ -848,6 +848,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_SUPPORTED_SELECTORS] =
                NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_supported_selectors,
                                       NL80211_MAX_SUPP_SELECTORS),
+       [NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -16476,6 +16477,66 @@ nl80211_set_ttlm(struct sk_buff *skb, struct genl_info *info)
        return rdev_set_ttlm(rdev, dev, &params);
 }
 
+static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_assoc_link links[IEEE80211_MLD_MAX_NUM_LINKS] = {};
+       unsigned int link_id;
+       u16 add_links, rem_links;
+       int err;
+
+       if (!wdev->valid_links)
+               return -EINVAL;
+
+       if (dev->ieee80211_ptr->conn_owner_nlportid &&
+           dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid)
+               return -EPERM;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
+               return -EOPNOTSUPP;
+
+       add_links = 0;
+       if (info->attrs[NL80211_ATTR_MLO_LINKS]) {
+               err = nl80211_process_links(rdev, links, NULL, 0, info);
+               if (err)
+                       return err;
+
+               for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
+                    link_id++) {
+                       if (!links[link_id].bss)
+                               continue;
+                       add_links |= BIT(link_id);
+               }
+       }
+
+       if (info->attrs[NL80211_ATTR_MLO_RECONF_REM_LINKS])
+               rem_links =
+                       nla_get_u16(info->attrs[NL80211_ATTR_MLO_RECONF_REM_LINKS]);
+       else
+               rem_links = 0;
+
+       /* Validate that existing links are not added, removed links are valid
+        * and don't allow adding and removing the same links
+        */
+       if ((add_links & rem_links) || !(add_links | rem_links) ||
+           (wdev->valid_links & add_links) ||
+           ((wdev->valid_links & rem_links) != rem_links)) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = cfg80211_assoc_ml_reconf(rdev, dev, links, rem_links);
+
+out:
+       for (link_id = 0; link_id < ARRAY_SIZE(links); link_id++)
+               cfg80211_put_bss(&rdev->wiphy, links[link_id].bss);
+
+       return err;
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -17668,6 +17729,12 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
        },
+       {
+               .cmd = NL80211_CMD_ASSOC_MLO_RECONF,
+               .doit = nl80211_assoc_ml_reconf,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
+       },
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {
@@ -18564,6 +18631,23 @@ void cfg80211_links_removed(struct net_device *dev, u16 link_mask)
 }
 EXPORT_SYMBOL(cfg80211_links_removed);
 
+void nl80211_mlo_reconf_add_done(struct net_device *dev,
+                                struct cfg80211_mlo_reconf_done_data *data)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct nl80211_mlme_event event = {
+               .cmd = NL80211_CMD_ASSOC_MLO_RECONF,
+               .buf = data->buf,
+               .buf_len = data->len,
+               .uapsd_queues = -1,
+       };
+
+       nl80211_send_mlme_event(rdev, dev, &event, GFP_KERNEL);
+}
+EXPORT_SYMBOL(nl80211_mlo_reconf_add_done);
+
 void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, const u8 *bssid,
                             gfp_t gfp)
index ffaab9a92e5bc75ddbce5fddec81e3a26a05f32b..5e25782af1e074be5e6a33798894457993dd5acd 100644 (file)
@@ -124,4 +124,7 @@ void cfg80211_free_coalesce(struct cfg80211_coalesce *coalesce);
 /* peer measurement */
 int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info);
 
+void nl80211_mlo_reconf_add_done(struct net_device *dev,
+                                struct cfg80211_mlo_reconf_done_data *data);
+
 #endif /* __NET_WIRELESS_NL80211_H */
index 8f2aa7e76c0a63726005e4fc26944203b5837549..2393a25577ad3f8f95bd8c6c3e38a111f949e3f5 100644 (file)
@@ -1547,4 +1547,23 @@ rdev_get_radio_mask(struct cfg80211_registered_device *rdev,
 
        return rdev->ops->get_radio_mask(wiphy, dev);
 }
+
+static inline int
+rdev_assoc_ml_reconf(struct cfg80211_registered_device *rdev,
+                    struct net_device *dev,
+                    struct cfg80211_assoc_link *add_links,
+                    u16 rem_links)
+{
+       struct wiphy *wiphy = &rdev->wiphy;
+       int ret = -EOPNOTSUPP;
+
+       trace_rdev_assoc_ml_reconf(wiphy, dev, add_links, rem_links);
+       if (rdev->ops->assoc_ml_reconf)
+               ret = rdev->ops->assoc_ml_reconf(wiphy, dev, add_links,
+                                                rem_links);
+       trace_rdev_return_int(wiphy, ret);
+
+       return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
index a57210c8087c2f0a22a993b76d8b0a8d57937475..627217e25c2f5000638dfda62858f521ca87d484 100644 (file)
@@ -4104,6 +4104,50 @@ TRACE_EVENT(cfg80211_links_removed,
                  __entry->link_mask)
 );
 
+TRACE_EVENT(cfg80211_mlo_reconf_add_done,
+       TP_PROTO(struct net_device *netdev, u16 link_mask,
+                const u8 *buf, size_t len),
+       TP_ARGS(netdev, link_mask, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __field(u16, link_mask)
+               __dynamic_array(u8, buf, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               __entry->link_mask = link_mask;
+               memcpy(__get_dynamic_array(buf), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", link_mask:0x%x",
+                 NETDEV_PR_ARG, __entry->link_mask)
+);
+
+TRACE_EVENT(rdev_assoc_ml_reconf,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_assoc_link *add_links,
+                u16 rem_links),
+       TP_ARGS(wiphy, netdev, add_links, rem_links),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               __field(u16, add_links)
+               __field(u16, rem_links)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               u32 i;
+
+               __entry->add_links = 0;
+               __entry->rem_links = rem_links;
+               for (i = 0; add_links && i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
+                       if (add_links[i].bss)
+                               __entry->add_links |= BIT(i);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", add_links=0x%x, rem_links=0x%x",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG,
+                 __entry->add_links, __entry->rem_links)
+);
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH