Commit 65c1c041 authored by Ilan Peer's avatar Ilan Peer Committed by Johannes Berg
Browse files

wifi: cfg80211: Add support for dynamic addition/removal of links



Add support for requesting dynamic addition/removal of links to the
current MLO association.

Signed-off-by: default avatarIlan Peer <ilan.peer@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250102161730.cef23352f2a2.I79c849974c494cb1cbf9e1b22a5d2d37395ff5ac@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 720fa448
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -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
+10 −0
Original line number Diff line number Diff line
@@ -1329,6 +1329,9 @@
 *      %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,
+4 −0
Original line number Diff line number Diff line
@@ -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
 *
+77 −0
Original line number Diff line number Diff line
@@ -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);
+84 −0
Original line number Diff line number Diff line
@@ -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)
Loading