08.06.2022 12:16, Veerendranath Jakkam пишет: > The MLO links used for connection with an MLD AP are decided by the > driver in case of SME offloaded to driver. > > Add support for the drivers to indicate the information of links used > for MLO connection in connect and roam callbacks, update the connected > links information in wdev from connect/roam result sent by driver. > Also, send the connected links information to userspace. > > Add a netlink flag attribute to indicate that userspace supports > handling of MLO connection. Drivers must not do MLO connection when this > flag is not set. This is to maintain backwards compatibility with older > supplicant versions which doesn't have support for MLO connection. > > Signed-off-by: Veerendranath Jakkam <quic_vjakkam@xxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- > drivers/net/wireless/ath/wil6210/wmi.c | 4 +- > .../broadcom/brcm80211/brcmfmac/cfg80211.c | 6 +- > drivers/net/wireless/rndis_wlan.c | 5 +- > drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4 +- > drivers/staging/wlan-ng/cfg80211.c | 2 +- > include/net/cfg80211.h | 84 +++-- > include/uapi/linux/nl80211.h | 6 + > net/wireless/mlme.c | 4 +- > net/wireless/nl80211.c | 120 ++++++- > net/wireless/sme.c | 391 +++++++++++++++------ > 11 files changed, 480 insertions(+), 148 deletions(-) > > diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c > index 33ed547..e11c7e9 100644 > --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c > +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c > @@ -807,7 +807,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, > cfg80211_put_bss(ar->wiphy, bss); > } else if (vif->sme_state == SME_CONNECTED) { > struct cfg80211_roam_info roam_info = { > - .bss = bss, > + .links[0].bss = bss, > .req_ie = assoc_req_ie, > .req_ie_len = assoc_req_len, > .resp_ie = assoc_resp_ie, > diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c > index 98b4c18..ea7bd40 100644 > --- a/drivers/net/wireless/ath/wil6210/wmi.c > +++ b/drivers/net/wireless/ath/wil6210/wmi.c > @@ -1822,8 +1822,8 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len) > freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); > > memset(&info, 0, sizeof(info)); > - info.channel = ieee80211_get_channel(wiphy, freq); > - info.bss = vif->bss; > + info.links[0].channel = ieee80211_get_channel(wiphy, freq); > + info.links[0].bss = vif->bss; > info.req_ie = assoc_req_ie; > info.req_ie_len = assoc_req_ie_len; > info.resp_ie = assoc_resp_ie; > diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c > index f6bd151..ab0d248 100644 > --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c > +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c > @@ -6017,8 +6017,8 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, > done: > kfree(buf); > > - roam_info.channel = notify_channel; > - roam_info.bssid = profile->bssid; > + roam_info.links[0].channel = notify_channel; > + roam_info.links[0].bssid = profile->bssid; > roam_info.req_ie = conn_info->req_ie; > roam_info.req_ie_len = conn_info->req_ie_len; > roam_info.resp_ie = conn_info->resp_ie; > @@ -6061,7 +6061,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, > } else { > conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; > } > - conn_params.bssid = profile->bssid; > + conn_params.links[0].bssid = profile->bssid; > conn_params.req_ie = conn_info->req_ie; > conn_params.req_ie_len = conn_info->req_ie_len; > conn_params.resp_ie = conn_info->resp_ie; > diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c > index ff24483..0552429 100644 > --- a/drivers/net/wireless/rndis_wlan.c > +++ b/drivers/net/wireless/rndis_wlan.c > @@ -2813,8 +2813,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) > resp_ie_len, 0, GFP_KERNEL); > } else { > struct cfg80211_roam_info roam_info = { > - .channel = get_current_channel(usbdev, NULL), > - .bssid = bssid, > + .links[0].channel = > + get_current_channel(usbdev, NULL), > + .links[0].bssid = bssid, > .req_ie = req_ie, > .req_ie_len = req_ie_len, > .resp_ie = resp_ie, > diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c > index 349aa3c..cf35125 100644 > --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c > +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c > @@ -450,8 +450,8 @@ void rtw_cfg80211_indicate_connect(struct adapter *padapter) > > notify_channel = ieee80211_get_channel(wiphy, freq); > > - roam_info.channel = notify_channel; > - roam_info.bssid = cur_network->network.mac_address; > + roam_info.links[0].channel = notify_channel; > + roam_info.links[0].bssid = cur_network->network.mac_address; > roam_info.req_ie = > pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2; > roam_info.req_ie_len = > diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c > index 7951bd6..fc7747b 100644 > --- a/drivers/staging/wlan-ng/cfg80211.c > +++ b/drivers/staging/wlan-ng/cfg80211.c > @@ -647,7 +647,7 @@ void prism2_disconnected(struct wlandevice *wlandev) > void prism2_roamed(struct wlandevice *wlandev) > { > struct cfg80211_roam_info roam_info = { > - .bssid = wlandev->bssid, > + .links[0].bssid = wlandev->bssid, > }; > > cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL); > diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h > index 1b52189..2ff521a 100644 > --- a/include/net/cfg80211.h > +++ b/include/net/cfg80211.h > @@ -2742,6 +2742,9 @@ struct cfg80211_auth_request { > * request (connect callback). > * @ASSOC_REQ_DISABLE_HE: Disable HE > * @ASSOC_REQ_DISABLE_EHT: Disable EHT > + * @CONNECT_REQ_MLO_SUPPORT: Userspace indicates support for handling MLD links. > + * Drivers shall disable MLO features for the current association if this > + * flag is not set. > */ > enum cfg80211_assoc_req_flags { > ASSOC_REQ_DISABLE_HT = BIT(0), > @@ -2750,6 +2753,7 @@ enum cfg80211_assoc_req_flags { > CONNECT_REQ_EXTERNAL_AUTH_SUPPORT = BIT(3), > ASSOC_REQ_DISABLE_HE = BIT(4), > ASSOC_REQ_DISABLE_EHT = BIT(5), > + CONNECT_REQ_MLO_SUPPORT = BIT(6), > }; > > /** > @@ -5737,12 +5741,13 @@ static inline void WARN_INVALID_LINK_ID(struct wireless_dev *wdev, > !(wdev->valid_links & BIT(link_id))); > } > > -#define for_each_valid_link(wdev, link_id) \ > - for (link_id = 0; \ > - link_id < ((wdev)->valid_links ? ARRAY_SIZE((wdev)->links) : 1); \ > - link_id++) \ > - if (!(wdev)->valid_links || \ > - ((wdev)->valid_links & BIT(link_id))) > +#define for_each_valid_link(link_info, link_id) \ > + for (link_id = 0; \ > + link_id < ((link_info)->valid_links ? \ > + ARRAY_SIZE((link_info)->links) : 1); \ > + link_id++) \ > + if (!(link_info)->valid_links || \ > + ((link_info)->valid_links & BIT(link_id))) > > /** > * DOC: Utility functions > @@ -7253,13 +7258,6 @@ struct cfg80211_fils_resp_params { > * indicate that this is a failure, but without a status code. > * @timeout_reason is used to report the reason for the timeout in that > * case. > - * @bssid: The BSSID of the AP (may be %NULL) > - * @bss: Entry of bss to which STA got connected to, can be obtained through > - * cfg80211_get_bss() (may be %NULL). But it is recommended to store the > - * bss from the connect_request and hold a reference to it and return > - * through this param to avoid a warning if the bss is expired during the > - * connection, esp. for those drivers implementing connect op. > - * Only one parameter among @bssid and @bss needs to be specified. > * @req_ie: Association request IEs (may be %NULL) > * @req_ie_len: Association request IEs length > * @resp_ie: Association response IEs (may be %NULL) > @@ -7271,17 +7269,41 @@ struct cfg80211_fils_resp_params { > * not known. This value is used only if @status < 0 to indicate that the > * failure is due to a timeout and not due to explicit rejection by the AP. > * This value is ignored in other cases (@status >= 0). > + * @valid_links: For MLO connection, BIT mask of the valid link ids. Otherwise > + * zero. > + * @ap_mld_addr: For MLO connection, MLD address of the AP. Otherwise %NULL. > + * @links : For MLO connection, contains link info for the valid links indicated > + * using @valid_links. For non-MLO connection, links[0] contains the > + * connected AP info. > + * @links.addr: For MLO connection, MAC address of the STA link. Otherwise > + * %NULL. > + * @links.bssid: For MLO connection, MAC address of the AP link. For non-MLO > + * connection, links[0].bssid points to the BSSID of the AP (may be %NULL). > + * @links.bss: For MLO connection, entry of bss to which STA link is connected. > + * For non-MLO connection, links[0].bss points to entry of bss to which STA > + * is connected. It can be obtained through cfg80211_get_bss() (may be > + * %NULL). It is recommended to store the bss from the connect_request and > + * hold a reference to it and return through this param to avoid a warning > + * if the bss is expired during the connection, esp. for those drivers > + * implementing connect op. Only one parameter among @bssid and @bss needs > + * to be specified. > */ > struct cfg80211_connect_resp_params { > int status; > - const u8 *bssid; > - struct cfg80211_bss *bss; > const u8 *req_ie; > size_t req_ie_len; > const u8 *resp_ie; > size_t resp_ie_len; > struct cfg80211_fils_resp_params fils; > enum nl80211_timeout_reason timeout_reason; > + > + const u8 *ap_mld_addr; > + u16 valid_links; > + struct { > + const u8 *addr; > + const u8 *bssid; > + struct cfg80211_bss *bss; > + } links[IEEE80211_MLD_MAX_NUM_LINKS]; > }; > > /** > @@ -7351,8 +7373,8 @@ cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, > > memset(¶ms, 0, sizeof(params)); > params.status = status; > - params.bssid = bssid; > - params.bss = bss; > + params.links[0].bssid = bssid; > + params.links[0].bss = bss; > params.req_ie = req_ie; > params.req_ie_len = req_ie_len; > params.resp_ie = resp_ie; > @@ -7423,24 +7445,40 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, > /** > * struct cfg80211_roam_info - driver initiated roaming information > * > - * @channel: the channel of the new AP > - * @bss: entry of bss to which STA got roamed (may be %NULL if %bssid is set) > - * @bssid: the BSSID of the new AP (may be %NULL if %bss is set) > * @req_ie: association request IEs (maybe be %NULL) > * @req_ie_len: association request IEs length > * @resp_ie: association response IEs (may be %NULL) > * @resp_ie_len: assoc response IEs length > * @fils: FILS related roaming information. > + * @valid_links: For MLO roaming, BIT mask of the new valid links is set. > + * Otherwise zero. > + * @ap_mld_addr: For MLO roaming, MLD address of the new AP. Otherwise %NULL. > + * @links : For MLO roaming, contains new link info for the valid links set in > + * @valid_links. For non-MLO roaming, links[0] contains the new AP info. > + * @links.addr: For MLO roaming, MAC address of the STA link. Otherwise %NULL. > + * @links.bssid: For MLO roaming, MAC address of the new AP link. For non-MLO > + * roaming, links[0].bssid points to the BSSID of the new AP. May be > + * %NULL if %links.bss is set. > + * @links.channel: the channel of the new AP. > + * @links.bss: For MLO roaming, entry of new bss to which STA link got > + * roamed. For non-MLO roaming, links[0].bss points to entry of bss to > + * which STA got roamed (may be %NULL if %links.bssid is set) > */ > struct cfg80211_roam_info { > - struct ieee80211_channel *channel; > - struct cfg80211_bss *bss; > - const u8 *bssid; > const u8 *req_ie; > size_t req_ie_len; > const u8 *resp_ie; > size_t resp_ie_len; > struct cfg80211_fils_resp_params fils; > + > + const u8 *ap_mld_addr; > + u16 valid_links; > + struct { > + const u8 *addr; > + const u8 *bssid; > + struct ieee80211_channel *channel; > + struct cfg80211_bss *bss; > + } links[IEEE80211_MLD_MAX_NUM_LINKS]; > }; > > /** > diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h > index 509253b..2c55414 100644 > --- a/include/uapi/linux/nl80211.h > +++ b/include/uapi/linux/nl80211.h > @@ -2688,6 +2688,10 @@ enum nl80211_commands { > * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some > * per-link information and a link ID. > * > + * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO > + * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not > + * included in NL80211_CMD_CONNECT drivers must not perform MLO connection. > + * > * @NUM_NL80211_ATTR: total number of nl80211_attrs available > * @NL80211_ATTR_MAX: highest attribute number currently defined > * @__NL80211_ATTR_AFTER_LAST: internal use > @@ -3205,6 +3209,8 @@ enum nl80211_attrs { > NL80211_ATTR_MLO_LINKS, > NL80211_ATTR_MLO_LINK_ID, > > + NL80211_ATTR_MLO_SUPPORT, > + > /* add attributes here, update the policy in nl80211.c */ > > __NL80211_ATTR_AFTER_LAST, > diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c > index fab2d62..4fdd306 100644 > --- a/net/wireless/mlme.c > +++ b/net/wireless/mlme.c > @@ -42,8 +42,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, > > memset(&cr, 0, sizeof(cr)); > cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); > - cr.bssid = mgmt->bssid; > - cr.bss = bss; > + cr.links[0].bssid = mgmt->bssid; > + cr.links[0].bss = bss; > cr.req_ie = req_ies; > cr.req_ie_len = req_ies_len; > cr.resp_ie = resp_ie; > diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c > index 9c8bf1b..e968792 100644 > --- a/net/wireless/nl80211.c > +++ b/net/wireless/nl80211.c > @@ -796,6 +796,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { > NLA_POLICY_NESTED_ARRAY(nl80211_policy), > [NL80211_ATTR_MLO_LINK_ID] = > NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), > + [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, > }; > > /* policy for the key attributes */ > @@ -11383,6 +11384,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) > connect.flags |= CONNECT_REQ_EXTERNAL_AUTH_SUPPORT; > } > > + if (nla_get_flag(info->attrs[NL80211_ATTR_MLO_SUPPORT])) > + connect.flags |= CONNECT_REQ_MLO_SUPPORT; > + > wdev_lock(dev->ieee80211_ptr); > > err = cfg80211_connect(rdev, dev, &connect, connkeys, > @@ -17134,10 +17138,29 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, > { > struct sk_buff *msg; > void *hdr; > + unsigned int link; > + size_t link_info_size = 0; > + const u8 *connected_addr = cr->valid_links ? > + cr->ap_mld_addr : cr->links[0].bssid; > + > + if (cr->valid_links) { > + for_each_valid_link(cr, link) { > + /* Nested attribute header */ > + link_info_size += NLA_HDRLEN; > + /* Link ID */ > + link_info_size += nla_total_size(sizeof(u8)); > + link_info_size += cr->links[link].addr ? > + nla_total_size(ETH_ALEN) : 0; > + link_info_size += (cr->links[link].bssid || > + cr->links[link].bss) ? > + nla_total_size(ETH_ALEN) : 0; > + } > + } > > msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + > cr->fils.kek_len + cr->fils.pmk_len + > - (cr->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); > + (cr->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, > + gfp); > if (!msg) > return; > > @@ -17149,8 +17172,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, > > if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || > nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || > - (cr->bssid && > - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) || > + (connected_addr && > + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr)) || > nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, > cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : > cr->status) || > @@ -17176,6 +17199,38 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, > nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid))))) > goto nla_put_failure; > > + if (cr->valid_links) { > + int i = 1; > + struct nlattr *nested; > + > + nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); > + if (!nested) > + goto nla_put_failure; > + > + for_each_valid_link(cr, link) { > + struct nlattr *nested_mlo_links; > + const u8 *bssid = cr->links[link].bss ? > + cr->links[link].bss->bssid : > + cr->links[link].bssid; > + > + nested_mlo_links = nla_nest_start(msg, i); > + if (!nested_mlo_links) > + goto nla_put_failure; > + > + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link) || > + (bssid && > + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || > + (cr->links[link].addr && > + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, > + cr->links[link].addr))) > + goto nla_put_failure; > + > + nla_nest_end(msg, nested_mlo_links); > + i++; > + } > + nla_nest_end(msg, nested); > + } > + > genlmsg_end(msg, hdr); > > genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, > @@ -17192,11 +17247,32 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, > { > struct sk_buff *msg; > void *hdr; > - const u8 *bssid = info->bss ? info->bss->bssid : info->bssid; > + size_t link_info_size = 0; > + unsigned int link; > + const u8 *connected_addr = info->ap_mld_addr ? > + info->ap_mld_addr : > + (info->links[0].bss ? > + info->links[0].bss->bssid : > + info->links[0].bssid); > + > + if (info->valid_links) { > + for_each_valid_link(info, link) { > + /* Nested attribute header */ > + link_info_size += NLA_HDRLEN; > + /* Link ID */ > + link_info_size += nla_total_size(sizeof(u8)); > + link_info_size += info->links[link].addr ? > + nla_total_size(ETH_ALEN) : 0; > + link_info_size += (info->links[link].bssid || > + info->links[link].bss) ? > + nla_total_size(ETH_ALEN) : 0; > + } > + } > > msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len + > info->fils.kek_len + info->fils.pmk_len + > - (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); > + (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + > + link_info_size, gfp); > if (!msg) > return; > > @@ -17208,7 +17284,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, > > if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || > nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || > - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) || > + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) || > (info->req_ie && > nla_put(msg, NL80211_ATTR_REQ_IE, info->req_ie_len, > info->req_ie)) || > @@ -17227,6 +17303,38 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, > nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid))) > goto nla_put_failure; > > + if (info->valid_links) { > + int i = 1; > + struct nlattr *nested; > + > + nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); > + if (!nested) > + goto nla_put_failure; > + > + for_each_valid_link(info, link) { > + struct nlattr *nested_mlo_links; > + const u8 *bssid = info->links[link].bss ? > + info->links[link].bss->bssid : > + info->links[link].bssid; > + > + nested_mlo_links = nla_nest_start(msg, i); > + if (!nested_mlo_links) > + goto nla_put_failure; > + > + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link) || > + (bssid && > + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || > + (info->links[link].addr && > + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, > + info->links[link].addr))) > + goto nla_put_failure; > + > + nla_nest_end(msg, nested_mlo_links); > + i++; > + } > + nla_nest_end(msg, nested); > + } > + > genlmsg_end(msg, hdr); > > genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, > diff --git a/net/wireless/sme.c b/net/wireless/sme.c > index 3560220..a11b99c 100644 > --- a/net/wireless/sme.c > +++ b/net/wireless/sme.c > @@ -258,7 +258,7 @@ void cfg80211_conn_work(struct work_struct *work) > > memset(&cr, 0, sizeof(cr)); > cr.status = -1; > - cr.bssid = bssid; > + cr.links[0].bssid = bssid; > cr.timeout_reason = treason; > __cfg80211_connect_result(wdev->netdev, &cr, false); > } > @@ -367,7 +367,7 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) > > memset(&cr, 0, sizeof(cr)); > cr.status = status_code; > - cr.bssid = mgmt->bssid; > + cr.links[0].bssid = mgmt->bssid; > cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; > __cfg80211_connect_result(wdev->netdev, &cr, false); > } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { > @@ -681,6 +681,20 @@ static void disconnect_work(struct work_struct *work) > > DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); > > +static void > +cfg80211_connect_result_release_bsses(struct wireless_dev *wdev, > + struct cfg80211_connect_resp_params *cr) > +{ > + unsigned int link; > + > + for_each_valid_link(cr, link) { > + if (!cr->links[link].bss) > + continue; > + cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss)); > + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); > + } > +} > + > /* > * API calls for drivers implementing connect/disconnect and > * SME event handling > @@ -698,21 +712,33 @@ void __cfg80211_connect_result(struct net_device *dev, > #ifdef CONFIG_CFG80211_WEXT > union iwreq_data wrqu; > #endif > + unsigned int link; > + const u8 *connected_addr; > + bool bss_not_found = false; > > ASSERT_WDEV_LOCK(wdev); > > if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && > - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { > - cfg80211_put_bss(wdev->wiphy, cr->bss); > - return; > + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) > + goto out; > + > + if (cr->valid_links) { > + if (WARN_ON(!cr->ap_mld_addr)) > + goto out; > + > + for_each_valid_link(cr, link) { > + if (WARN_ON(!cr->links[link].addr)) > + goto out; > + } > } > > wdev->unprot_beacon_reported = 0; > nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, > GFP_KERNEL); > + connected_addr = cr->valid_links ? cr->ap_mld_addr : cr->links[0].bssid; > > #ifdef CONFIG_CFG80211_WEXT > - if (wextev) { > + if (wextev && !cr->valid_links) { > if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { > memset(&wrqu, 0, sizeof(wrqu)); > wrqu.data.length = cr->req_ie_len; > @@ -729,23 +755,38 @@ void __cfg80211_connect_result(struct net_device *dev, > > memset(&wrqu, 0, sizeof(wrqu)); > wrqu.ap_addr.sa_family = ARPHRD_ETHER; > - if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { > - memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); > - memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); > + if (connected_addr && cr->status == WLAN_STATUS_SUCCESS) { > + memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); > + memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); > wdev->wext.prev_bssid_valid = true; > } > wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); > } > #endif > > - if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { > - WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); > - cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, > - wdev->u.client.ssid, wdev->u.client.ssid_len, > - wdev->conn_bss_type, > - IEEE80211_PRIVACY_ANY); > - if (cr->bss) > - cfg80211_hold_bss(bss_from_pub(cr->bss)); > + if (cr->status == WLAN_STATUS_SUCCESS) { > + for_each_valid_link(cr, link) { > + if (WARN_ON_ONCE(!cr->links[link].bss)) > + break; > + } > + > + for_each_valid_link(cr, link) { > + if (cr->links[link].bss) > + continue; > + > + cr->links[link].bss = > + cfg80211_get_bss(wdev->wiphy, NULL, > + cr->links[link].bssid, > + wdev->u.client.ssid, > + wdev->u.client.ssid_len, > + wdev->conn_bss_type, > + IEEE80211_PRIVACY_ANY); > + if (!cr->links[link].bss) { > + bss_not_found = true; > + break; > + } > + cfg80211_hold_bss(bss_from_pub(cr->links[link].bss)); > + } > } > > cfg80211_wdev_release_bsses(wdev); > @@ -755,26 +796,40 @@ void __cfg80211_connect_result(struct net_device *dev, > wdev->connect_keys = NULL; > wdev->u.client.ssid_len = 0; > wdev->conn_owner_nlportid = 0; > - if (cr->bss) { > - cfg80211_unhold_bss(bss_from_pub(cr->bss)); > - cfg80211_put_bss(wdev->wiphy, cr->bss); > - } > + cfg80211_connect_result_release_bsses(wdev, cr); > cfg80211_sme_free(wdev); > return; > } > > - if (WARN_ON(!cr->bss)) > + if (WARN_ON(bss_not_found)) { > + cfg80211_connect_result_release_bsses(wdev, cr); > return; > + } > > - wdev->links[0].client.current_bss = bss_from_pub(cr->bss); > + memset(wdev->links, 0, sizeof(wdev->links)); > + wdev->valid_links = cr->valid_links; > + for_each_valid_link(cr, link) > + wdev->links[link].client.current_bss = > + bss_from_pub(cr->links[link].bss); > wdev->connected = true; > - ether_addr_copy(wdev->u.client.connected_addr, cr->bss->bssid); > + ether_addr_copy(wdev->u.client.connected_addr, connected_addr); > + if (cr->valid_links) { > + for_each_valid_link(cr, link) > + memcpy(wdev->links[link].addr, cr->links[link].addr, > + ETH_ALEN); > + } > > if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP)) > cfg80211_upload_connect_keys(wdev); > > rcu_read_lock(); > - country_elem = ieee80211_bss_get_elem(cr->bss, WLAN_EID_COUNTRY); > + for_each_valid_link(cr, link) { > + country_elem = > + ieee80211_bss_get_elem(cr->links[link].bss, > + WLAN_EID_COUNTRY); > + if (country_elem) > + break; > + } > if (!country_elem) { > rcu_read_unlock(); > return; > @@ -787,12 +842,60 @@ void __cfg80211_connect_result(struct net_device *dev, > if (!country_data) > return; > > - regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, > + regulatory_hint_country_ie(wdev->wiphy, > + cr->links[link].bss->channel->band, > country_data, country_datalen); > kfree(country_data); > + > + return; > +out: > + for_each_valid_link(cr, link) > + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); > } > > -/* Consumes bss object one way or another */ > +static void cfg80211_update_link_bss(struct wireless_dev *wdev, > + struct cfg80211_bss **bss) > +{ > + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); > + struct cfg80211_internal_bss *ibss; > + > + if (!*bss) > + return; > + > + ibss = bss_from_pub(*bss); > + if (list_empty(&ibss->list)) { > + struct cfg80211_bss *found = NULL, *tmp = *bss; > + > + found = cfg80211_get_bss(wdev->wiphy, NULL, > + (*bss)->bssid, > + wdev->u.client.ssid, > + wdev->u.client.ssid_len, > + wdev->conn_bss_type, > + IEEE80211_PRIVACY_ANY); > + if (found) { > + /* The same BSS is already updated so use it > + * instead, as it has latest info. > + */ > + *bss = found; > + } else { > + /* Update with BSS provided by driver, it will > + * be freshly added and ref cnted, we can free > + * the old one. > + * > + * signal_valid can be false, as we are not > + * expecting the BSS to be found. > + * > + * keep the old timestamp to avoid confusion > + */ > + cfg80211_bss_update(rdev, ibss, false, > + ibss->ts); > + } > + > + cfg80211_put_bss(wdev->wiphy, tmp); > + } > +} > + > +/* Consumes bss object(s) one way or another */ > void cfg80211_connect_done(struct net_device *dev, > struct cfg80211_connect_resp_params *params, > gfp_t gfp) > @@ -802,55 +905,34 @@ void cfg80211_connect_done(struct net_device *dev, > struct cfg80211_event *ev; > unsigned long flags; > u8 *next; > + size_t link_info_size = 0; > + unsigned int link; > > - if (params->bss) { > - struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); > - > - if (list_empty(&ibss->list)) { > - struct cfg80211_bss *found = NULL, *tmp = params->bss; > - > - found = cfg80211_get_bss(wdev->wiphy, NULL, > - params->bss->bssid, > - wdev->u.client.ssid, wdev->u.client.ssid_len, > - wdev->conn_bss_type, > - IEEE80211_PRIVACY_ANY); > - if (found) { > - /* The same BSS is already updated so use it > - * instead, as it has latest info. > - */ > - params->bss = found; > - } else { > - /* Update with BSS provided by driver, it will > - * be freshly added and ref cnted, we can free > - * the old one. > - * > - * signal_valid can be false, as we are not > - * expecting the BSS to be found. > - * > - * keep the old timestamp to avoid confusion > - */ > - cfg80211_bss_update(rdev, ibss, false, > - ibss->ts); > - } > - > - cfg80211_put_bss(wdev->wiphy, tmp); > - } > + for_each_valid_link(params, link) { > + cfg80211_update_link_bss(wdev, ¶ms->links[link].bss); > + link_info_size += params->links[link].bssid ? ETH_ALEN : 0; > + link_info_size += params->links[link].addr ? ETH_ALEN : 0; > } > > - ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + > + ev = kzalloc(sizeof(*ev) + (params->ap_mld_addr ? ETH_ALEN : 0) + > params->req_ie_len + params->resp_ie_len + > params->fils.kek_len + params->fils.pmk_len + > - (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); > + (params->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, > + gfp); > + > if (!ev) { > - cfg80211_put_bss(wdev->wiphy, params->bss); > + for_each_valid_link(params, link) > + cfg80211_put_bss(wdev->wiphy, > + params->links[link].bss); > return; > } > > ev->type = EVENT_CONNECT_RESULT; > next = ((u8 *)ev) + sizeof(*ev); > - if (params->bssid) { > - ev->cr.bssid = next; > - memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); > + if (params->ap_mld_addr) { > + ev->cr.ap_mld_addr = next; > + memcpy((void *)ev->cr.ap_mld_addr, params->ap_mld_addr, > + ETH_ALEN); > next += ETH_ALEN; > } > if (params->req_ie_len) { > @@ -890,9 +972,28 @@ void cfg80211_connect_done(struct net_device *dev, > ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num; > if (params->fils.update_erp_next_seq_num) > ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num; > - if (params->bss) > - cfg80211_hold_bss(bss_from_pub(params->bss)); > - ev->cr.bss = params->bss; > + ev->cr.valid_links = params->valid_links; > + for_each_valid_link(params, link) { > + if (params->links[link].bss) > + cfg80211_hold_bss( > + bss_from_pub(params->links[link].bss)); > + ev->cr.links[link].bss = params->links[link].bss; > + > + if (params->links[link].addr) { > + ev->cr.links[link].addr = next; > + memcpy((void *)ev->cr.links[link].addr, > + params->links[link].addr, > + ETH_ALEN); > + next += ETH_ALEN; > + } > + if (params->links[link].bssid) { > + ev->cr.links[link].bssid = next; > + memcpy((void *)ev->cr.links[link].bssid, > + params->links[link].bssid, > + ETH_ALEN); > + next += ETH_ALEN; > + } > + } > ev->cr.status = params->status; > ev->cr.timeout_reason = params->timeout_reason; > > @@ -910,6 +1011,9 @@ void __cfg80211_roamed(struct wireless_dev *wdev, > #ifdef CONFIG_CFG80211_WEXT > union iwreq_data wrqu; > #endif > + unsigned int link; > + const u8 *connected_addr; > + > ASSERT_WDEV_LOCK(wdev); > > if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && > @@ -919,48 +1023,76 @@ void __cfg80211_roamed(struct wireless_dev *wdev, > if (WARN_ON(!wdev->connected)) > goto out; > > + if (info->valid_links) { > + if (WARN_ON(!info->ap_mld_addr)) > + goto out; > + > + for_each_valid_link(info, link) { > + if (WARN_ON(!info->links[link].addr)) > + goto out; > + } > + } > + > cfg80211_wdev_release_bsses(wdev); > > - if (WARN_ON(!info->bss)) > - return; > + for_each_valid_link(info, link) { > + if (WARN_ON(!info->links[link].bss)) > + goto out; > + } > > - cfg80211_hold_bss(bss_from_pub(info->bss)); > - wdev->links[0].client.current_bss = bss_from_pub(info->bss); > - ether_addr_copy(wdev->u.client.connected_addr, info->bss->bssid); > + memset(wdev->links, 0, sizeof(wdev->links)); > + wdev->valid_links = info->valid_links; > + for_each_valid_link(info, link) { > + cfg80211_hold_bss(bss_from_pub(info->links[link].bss)); > + wdev->links[link].client.current_bss = > + bss_from_pub(info->links[link].bss); > + } > > + connected_addr = info->valid_links ? > + info->ap_mld_addr : > + info->links[0].bss->bssid; > + ether_addr_copy(wdev->u.client.connected_addr, connected_addr); > + if (info->valid_links) { > + for_each_valid_link(info, link) > + memcpy(wdev->links[link].addr, info->links[link].addr, > + ETH_ALEN); > + } > wdev->unprot_beacon_reported = 0; > nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), > wdev->netdev, info, GFP_KERNEL); > > #ifdef CONFIG_CFG80211_WEXT > - if (info->req_ie) { > - memset(&wrqu, 0, sizeof(wrqu)); > - wrqu.data.length = info->req_ie_len; > - wireless_send_event(wdev->netdev, IWEVASSOCREQIE, > - &wrqu, info->req_ie); > - } > + if (!info->valid_links) { > + if (info->req_ie) { > + memset(&wrqu, 0, sizeof(wrqu)); > + wrqu.data.length = info->req_ie_len; > + wireless_send_event(wdev->netdev, IWEVASSOCREQIE, > + &wrqu, info->req_ie); > + } > + > + if (info->resp_ie) { > + memset(&wrqu, 0, sizeof(wrqu)); > + wrqu.data.length = info->resp_ie_len; > + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, > + &wrqu, info->resp_ie); > + } > > - if (info->resp_ie) { > memset(&wrqu, 0, sizeof(wrqu)); > - wrqu.data.length = info->resp_ie_len; > - wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, > - &wrqu, info->resp_ie); > + wrqu.ap_addr.sa_family = ARPHRD_ETHER; > + memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); > + memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); > + wdev->wext.prev_bssid_valid = true; > + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); > } > - > - memset(&wrqu, 0, sizeof(wrqu)); > - wrqu.ap_addr.sa_family = ARPHRD_ETHER; > - memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN); > - memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN); > - wdev->wext.prev_bssid_valid = true; > - wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); > #endif > > return; > out: > - cfg80211_put_bss(wdev->wiphy, info->bss); > + for_each_valid_link(info, link) > + cfg80211_put_bss(wdev->wiphy, info->links[link].bss); > } > > -/* Consumes info->bss object one way or another */ > +/* Consumes info->links.bss object(s) one way or another */ > void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, > gfp_t gfp) > { > @@ -969,25 +1101,41 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, > struct cfg80211_event *ev; > unsigned long flags; > u8 *next; > + unsigned int link; > + size_t link_info_size = 0; > + bool bss_not_found = false; > + > + for_each_valid_link(info, link) { > + link_info_size += info->links[link].addr ? ETH_ALEN : 0; > + link_info_size += info->links[link].bssid ? ETH_ALEN : 0; > > - if (!info->bss) { > - info->bss = cfg80211_get_bss(wdev->wiphy, info->channel, > - info->bssid, wdev->u.client.ssid, > - wdev->u.client.ssid_len, > - wdev->conn_bss_type, > - IEEE80211_PRIVACY_ANY); > + if (info->links[link].bss) > + continue; > + > + info->links[link].bss = > + cfg80211_get_bss(wdev->wiphy, > + info->links[link].channel, > + info->links[link].bssid, > + wdev->u.client.ssid, > + wdev->u.client.ssid_len, > + wdev->conn_bss_type, > + IEEE80211_PRIVACY_ANY); > + > + if (!info->links[link].bss) { > + bss_not_found = true; > + break; > + } > } > > - if (WARN_ON(!info->bss)) > - return; > + if (WARN_ON(bss_not_found)) > + goto out; > > ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len + > info->fils.kek_len + info->fils.pmk_len + > - (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); > - if (!ev) { > - cfg80211_put_bss(wdev->wiphy, info->bss); > - return; > - } > + (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + > + (info->ap_mld_addr ? ETH_ALEN : 0) + link_info_size, gfp); > + if (!ev) > + goto out; > > ev->type = EVENT_ROAMED; > next = ((u8 *)ev) + sizeof(*ev); > @@ -1027,12 +1175,43 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, > ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num; > if (info->fils.update_erp_next_seq_num) > ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; > - ev->rm.bss = info->bss; > + if (info->ap_mld_addr) { > + ev->rm.ap_mld_addr = next; > + memcpy((void *)ev->rm.ap_mld_addr, info->ap_mld_addr, > + ETH_ALEN); > + next += ETH_ALEN; > + } > + ev->rm.valid_links = info->valid_links; > + for_each_valid_link(info, link) { > + ev->rm.links[link].bss = info->links[link].bss; > + > + if (info->links[link].addr) { > + ev->rm.links[link].addr = next; > + memcpy((void *)ev->rm.links[link].addr, > + info->links[link].addr, > + ETH_ALEN); > + next += ETH_ALEN; > + } > + > + if (info->links[link].bssid) { > + ev->rm.links[link].bssid = next; > + memcpy((void *)ev->rm.links[link].bssid, > + info->links[link].bssid, > + ETH_ALEN); > + next += ETH_ALEN; > + } > + } > > spin_lock_irqsave(&wdev->event_lock, flags); > list_add_tail(&ev->list, &wdev->event_list); > spin_unlock_irqrestore(&wdev->event_lock, flags); > queue_work(cfg80211_wq, &rdev->event_work); > + > + return; > +out: > + for_each_valid_link(info, link) > + cfg80211_put_bss(wdev->wiphy, info->links[link].bss); > + > } > EXPORT_SYMBOL(cfg80211_roamed); > Hello, On linux-next and using BRCMFAC driver I'm getting this noisy warning after connecting to a WiFi network. It should be caused by this patch in accordance to the git blame. Any suggestions how to fix it? ------------[ cut here ]------------ WARNING: CPU: 0 PID: 8 at net/wireless/sme.c:786 __cfg80211_connect_result+0x540/0x658 Modules linked in: CPU: 0 PID: 8 Comm: kworker/u4:0 Not tainted 5.19.0-rc8-next-20220728-00129-gbea14adffbdd #22 Hardware name: NVIDIA Tegra SoC (Flattened Device Tree) Workqueue: cfg80211 cfg80211_event_work Backtrace: dump_backtrace from show_stack+0x20/0x24 r7:c0f07d74 r6:00000009 r5:600d0113 r4:c147e94c show_stack from dump_stack_lvl+0x48/0x54 dump_stack_lvl from dump_stack+0x18/0x1c r5:00000312 r4:c15660cc dump_stack from __warn+0xd4/0x160 __warn from warn_slowpath_fmt+0x84/0xa0 r8:00000009 r7:c0f07d74 r6:00000312 r5:c15660cc r4:00000000 warn_slowpath_fmt from __cfg80211_connect_result+0x540/0x658 r9:c5328255 r8:c532800c r7:c1dd4b40 r6:c2e4b800 r5:c3838004 r4:c532800c __cfg80211_connect_result from cfg80211_process_wdev_events+0x104/0x160 r10:00000100 r9:00000122 r8:c3838070 r7:c3838028 r6:c3838078 r5:c3838004 r4:c5328000 cfg80211_process_wdev_events from cfg80211_process_rdev_events+0x34/0x48 r10:c1e89605 r9:c1dd4b40 r8:c1883520 r7:c1e89600 r6:c1c06a00 r5:c3858508 r4:c3838004 cfg80211_process_rdev_events from cfg80211_event_work+0x2c/0x38 r5:c3858220 r4:c38580cc cfg80211_event_work from process_one_work+0x21c/0x544 r5:c1e3d100 r4:c38580cc process_one_work from worker_thread+0x70/0x5a0 r10:00000088 r9:c1c06a00 r8:c1703d40 r7:c1c06a1c r6:c1e3d118 r5:c1c06a00 r4:c1e3d100 worker_thread from kthread+0x100/0x120 r10:00000000 r9:f080de44 r8:c1d4bd80 r7:c1e3d100 r6:c014c570 r5:c1d4b900 r4:c1dd4b40 kthread from ret_from_fork+0x14/0x2c Exception stack(0xf0829fb0 to 0xf0829ff8)