wifi: rtw89: implement ops of change vif/sta links

To support MLO, implement change_vif_links() and change_sta_links() ops.
Basically, we follow arguments to set/clear links. One special thing is
that when vif is idle, i.e. no connection, link id 0 is set up by us for
default uses. So, when bitmap of vif links change from 0x0 to non-zero,
we clear the default one first. And when bitmap of vif links change from
non-zero to 0x0, we set up a default one at the end.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20241120034054.13575-4-pkshih@realtek.com
This commit is contained in:
Zong-Zhe Yang
2024-11-27 11:10:16 +08:00
committed by Ping-Ke Shih
parent c821a8af43
commit d2b387bdca
2 changed files with 238 additions and 5 deletions
+17
View File
@@ -5700,10 +5700,17 @@ struct rtw89_dev {
u8 priv[] __aligned(sizeof(void *));
};
struct rtw89_link_conf_container {
struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
};
#define RTW89_VIF_IDLE_LINK_ID 0
struct rtw89_vif {
struct rtw89_dev *rtwdev;
struct list_head list;
struct list_head mgnt_entry;
struct rtw89_link_conf_container __rcu *snap_link_confs;
u8 mac_addr[ETH_ALEN];
__be32 ip_addr;
@@ -6273,9 +6280,19 @@ static inline struct ieee80211_bss_conf *
__rtw89_vif_rcu_dereference_link(struct rtw89_vif_link *rtwvif_link, bool *nolink)
{
struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;
struct rtw89_link_conf_container *snap;
struct ieee80211_bss_conf *bss_conf;
snap = rcu_dereference(rtwvif->snap_link_confs);
if (snap) {
bss_conf = snap->link_conf[rtwvif_link->link_id];
goto out;
}
bss_conf = rcu_dereference(vif->link_conf[rtwvif_link->link_id]);
out:
if (unlikely(!bss_conf)) {
*nolink = true;
return &vif->bss_conf;
+221 -5
View File
@@ -202,7 +202,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
rtw89_traffic_stats_init(rtwdev, &rtwvif->stats);
rtwvif_link = rtw89_vif_set_link(rtwvif, 0);
rtwvif_link = rtw89_vif_set_link(rtwvif, RTW89_VIF_IDLE_LINK_ID);
if (!rtwvif_link) {
ret = -EINVAL;
goto release_port;
@@ -218,7 +218,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
return 0;
unset_link:
rtw89_vif_unset_link(rtwvif, 0);
rtw89_vif_unset_link(rtwvif, RTW89_VIF_IDLE_LINK_ID);
release_port:
list_del_init(&rtwvif->list);
rtw89_core_release_bit_map(rtwdev->hw_port, port);
@@ -246,17 +246,17 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&rtwdev->mutex);
rtwvif_link = rtwvif->links[0];
rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID];
if (unlikely(!rtwvif_link)) {
rtw89_err(rtwdev,
"%s: rtwvif link (link_id %u) is not active\n",
__func__, 0);
__func__, RTW89_VIF_IDLE_LINK_ID);
goto bottom;
}
__rtw89_ops_remove_iface_link(rtwdev, rtwvif_link);
rtw89_vif_unset_link(rtwvif, 0);
rtw89_vif_unset_link(rtwvif, RTW89_VIF_IDLE_LINK_ID);
bottom:
list_del_init(&rtwvif->list);
@@ -1508,6 +1508,220 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw,
return rtw89_can_work_on_links(rtwdev, vif, active_links);
}
static void __rtw89_ops_clr_vif_links(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif,
unsigned long clr_links)
{
struct rtw89_vif_link *rtwvif_link;
unsigned int link_id;
for_each_set_bit(link_id, &clr_links, IEEE80211_MLD_MAX_NUM_LINKS) {
rtwvif_link = rtwvif->links[link_id];
if (unlikely(!rtwvif_link))
continue;
__rtw89_ops_remove_iface_link(rtwdev, rtwvif_link);
rtw89_vif_unset_link(rtwvif, link_id);
}
}
static int __rtw89_ops_set_vif_links(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif,
unsigned long set_links)
{
struct rtw89_vif_link *rtwvif_link;
unsigned int link_id;
int ret;
for_each_set_bit(link_id, &set_links, IEEE80211_MLD_MAX_NUM_LINKS) {
rtwvif_link = rtw89_vif_set_link(rtwvif, link_id);
if (!rtwvif_link)
return -EINVAL;
ret = __rtw89_ops_add_iface_link(rtwdev, rtwvif_link);
if (ret) {
rtw89_err(rtwdev, "%s: failed to add iface (link id %u)\n",
__func__, link_id);
return ret;
}
}
return 0;
}
static
int rtw89_ops_change_vif_links(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 old_links, u16 new_links,
struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
{
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
unsigned long clr_links = old_links & ~new_links;
unsigned long set_links = new_links & ~old_links;
bool removing_links = !old_links || clr_links;
struct rtw89_link_conf_container *snap;
int ret = 0;
int i;
guard(mutex)(&rtwdev->mutex);
rtw89_debug(rtwdev, RTW89_DBG_STATE,
"%s: old_links (0x%08x) -> new_links (0x%08x)\n",
__func__, old_links, new_links);
if (!rtw89_can_work_on_links(rtwdev, vif, new_links))
return -EOPNOTSUPP;
if (removing_links) {
snap = kzalloc(sizeof(*snap), GFP_KERNEL);
if (!snap)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(snap->link_conf); i++)
snap->link_conf[i] = old[i];
rcu_assign_pointer(rtwvif->snap_link_confs, snap);
}
/* might depend on @snap; don't change order */
rtw89_leave_ips_by_hwflags(rtwdev);
if (rtwdev->scanning)
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
if (!old_links)
__rtw89_ops_clr_vif_links(rtwdev, rtwvif,
BIT(RTW89_VIF_IDLE_LINK_ID));
else if (clr_links)
__rtw89_ops_clr_vif_links(rtwdev, rtwvif, clr_links);
if (removing_links) {
/* @snap is required if and only if during removing links.
* However, it's done here. So, cleanup @snap immediately.
*/
rcu_assign_pointer(rtwvif->snap_link_confs, NULL);
/* The pointers in @old will free after this function return,
* so synchronously wait for all readers of snap to be done.
*/
synchronize_rcu();
kfree(snap);
}
if (set_links) {
ret = __rtw89_ops_set_vif_links(rtwdev, rtwvif, set_links);
if (ret)
__rtw89_ops_clr_vif_links(rtwdev, rtwvif, set_links);
} else if (!new_links) {
ret = __rtw89_ops_set_vif_links(rtwdev, rtwvif,
BIT(RTW89_VIF_IDLE_LINK_ID));
if (ret)
__rtw89_ops_clr_vif_links(rtwdev, rtwvif,
BIT(RTW89_VIF_IDLE_LINK_ID));
}
rtw89_enter_ips_by_hwflags(rtwdev);
return ret;
}
static void __rtw89_ops_clr_sta_links(struct rtw89_dev *rtwdev,
struct rtw89_sta *rtwsta,
unsigned long clr_links)
{
struct rtw89_vif_link *rtwvif_link;
struct rtw89_sta_link *rtwsta_link;
unsigned int link_id;
for_each_set_bit(link_id, &clr_links, IEEE80211_MLD_MAX_NUM_LINKS) {
rtwsta_link = rtwsta->links[link_id];
if (unlikely(!rtwsta_link))
continue;
rtwvif_link = rtwsta_link->rtwvif_link;
rtw89_core_sta_link_disassoc(rtwdev, rtwvif_link, rtwsta_link);
rtw89_core_sta_link_disconnect(rtwdev, rtwvif_link, rtwsta_link);
rtw89_core_sta_link_remove(rtwdev, rtwvif_link, rtwsta_link);
rtw89_sta_unset_link(rtwsta, link_id);
}
}
static int __rtw89_ops_set_sta_links(struct rtw89_dev *rtwdev,
struct rtw89_sta *rtwsta,
unsigned long set_links)
{
struct rtw89_vif_link *rtwvif_link;
struct rtw89_sta_link *rtwsta_link;
unsigned int link_id;
int ret;
for_each_set_bit(link_id, &set_links, IEEE80211_MLD_MAX_NUM_LINKS) {
rtwsta_link = rtw89_sta_set_link(rtwsta, link_id);
if (!rtwsta_link)
return -EINVAL;
rtwvif_link = rtwsta_link->rtwvif_link;
ret = rtw89_core_sta_link_add(rtwdev, rtwvif_link, rtwsta_link);
if (ret) {
rtw89_err(rtwdev, "%s: failed to add sta (link id %u)\n",
__func__, link_id);
return ret;
}
rtw89_vif_type_mapping(rtwvif_link, true);
ret = rtw89_core_sta_link_assoc(rtwdev, rtwvif_link, rtwsta_link);
if (ret) {
rtw89_err(rtwdev, "%s: failed to assoc sta (link id %u)\n",
__func__, link_id);
return ret;
}
__rtw89_ops_bss_link_assoc(rtwdev, rtwvif_link);
}
return 0;
}
static
int rtw89_ops_change_sta_links(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u16 old_links, u16 new_links)
{
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
unsigned long clr_links = old_links & ~new_links;
unsigned long set_links = new_links & ~old_links;
int ret = 0;
guard(mutex)(&rtwdev->mutex);
rtw89_debug(rtwdev, RTW89_DBG_STATE,
"%s: old_links (0x%08x) -> new_links (0x%08x)\n",
__func__, old_links, new_links);
if (!rtw89_can_work_on_links(rtwdev, vif, new_links))
return -EOPNOTSUPP;
rtw89_leave_ps_mode(rtwdev);
if (clr_links)
__rtw89_ops_clr_sta_links(rtwdev, rtwsta, clr_links);
if (set_links) {
ret = __rtw89_ops_set_sta_links(rtwdev, rtwsta, set_links);
if (ret)
__rtw89_ops_clr_sta_links(rtwdev, rtwsta, set_links);
}
return ret;
}
#ifdef CONFIG_PM
static int rtw89_ops_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
@@ -1636,6 +1850,8 @@ const struct ieee80211_ops rtw89_ops = {
.link_sta_rc_update = rtw89_ops_sta_rc_update,
.set_tid_config = rtw89_ops_set_tid_config,
.can_activate_links = rtw89_ops_can_activate_links,
.change_vif_links = rtw89_ops_change_vif_links,
.change_sta_links = rtw89_ops_change_sta_links,
#ifdef CONFIG_PM
.suspend = rtw89_ops_suspend,
.resume = rtw89_ops_resume,