Skip to content

Commit

Permalink
wifi: mac80211: implement link switching
Browse files Browse the repository at this point in the history
Implement an API function and debugfs file to switch
active links.

Change-Id: I92385ba882ec984a9a2ad18293173436657e82aa
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
jmberg-intel committed Sep 1, 2022
1 parent 3d6496a commit 43af0cd
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 0 deletions.
28 changes: 28 additions & 0 deletions include/net/mac80211.h
Expand Up @@ -7128,4 +7128,32 @@ static inline bool ieee80211_is_tx_data(struct sk_buff *skb)
ieee80211_is_data(hdr->frame_control);
}

/**
* ieee80211_set_active_links - set active links in client mode
* @vif: interface to set active links on
* @active_links: the new active links bitmap
*
* This changes the active links on an interface. The interface
* must be in client mode (in AP mode, all links are always active),
* and @active_links must be a subset of the vif's valid_links.
*
* If a link is switched off and another is switched on at the same
* time (e.g. active_links going from 0x1 to 0x10) then you will get
* a sequence of calls like
* - change_vif_links(0x11)
* - unassign_vif_chanctx(link_id=0)
* - change_sta_links(0x11) for each affected STA (the AP)
* (TDLS connections on now inactive links should be torn down)
* - remove group keys on the old link (link_id 0)
* - add new group keys (GTK/IGTK/BIGTK) on the new link (link_id 4)
* - change_sta_links(0x10) for each affected STA (the AP)
* - assign_vif_chanctx(link_id=4)
* - change_vif_links(0x10)
*
* Note: This function acquires some mac80211 locks and must not
* be called with any driver locks held that could cause a
* lock dependency inversion. Best call it without locks.
*/
int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links);

#endif /* MAC80211_H */
26 changes: 26 additions & 0 deletions net/mac80211/debugfs_netdev.c
Expand Up @@ -570,6 +570,30 @@ static ssize_t ieee80211_if_parse_tsf(
}
IEEE80211_IF_FILE_RW(tsf);

static ssize_t ieee80211_if_fmt_valid_links(const struct ieee80211_sub_if_data *sdata,
char *buf, int buflen)
{
return snprintf(buf, buflen, "0x%x\n", sdata->vif.valid_links);
}
IEEE80211_IF_FILE_R(valid_links);

static ssize_t ieee80211_if_fmt_active_links(const struct ieee80211_sub_if_data *sdata,
char *buf, int buflen)
{
return snprintf(buf, buflen, "0x%x\n", sdata->vif.active_links);
}

static ssize_t ieee80211_if_parse_active_links(struct ieee80211_sub_if_data *sdata,
const char *buf, int buflen)
{
u16 active_links;

if (kstrtou16(buf, 0, &active_links))
return -EINVAL;

return ieee80211_set_active_links(&sdata->vif, active_links) ?: buflen;
}
IEEE80211_IF_FILE_RW(active_links);

#ifdef CONFIG_MAC80211_MESH
IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
Expand Down Expand Up @@ -670,6 +694,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD_MODE(uapsd_queues, 0600);
DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
DEBUGFS_ADD_MODE(tdls_wider_bw, 0600);
DEBUGFS_ADD_MODE(valid_links, 0200);
DEBUGFS_ADD_MODE(active_links, 0600);
}

static void add_ap_files(struct ieee80211_sub_if_data *sdata)
Expand Down
34 changes: 34 additions & 0 deletions net/mac80211/key.c
Expand Up @@ -1445,3 +1445,37 @@ void ieee80211_key_replay(struct ieee80211_key_conf *keyconf)
}
}
EXPORT_SYMBOL_GPL(ieee80211_key_replay);

int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata,
unsigned long del_links_mask,
unsigned long add_links_mask)
{
struct ieee80211_key *key;
int ret;

list_for_each_entry(key, &sdata->key_list, list) {
if (key->conf.link_id < 0 ||
!(del_links_mask & BIT(key->conf.link_id)))
continue;

/* shouldn't happen for per-link keys */
WARN_ON(key->sta);

ieee80211_key_disable_hw_accel(key);
}

list_for_each_entry(key, &sdata->key_list, list) {
if (key->conf.link_id < 0 ||
!(add_links_mask & BIT(key->conf.link_id)))
continue;

/* shouldn't happen for per-link keys */
WARN_ON(key->sta);

ret = ieee80211_key_enable_hw_accel(key);
if (ret)
return ret;
}

return 0;
}
3 changes: 3 additions & 0 deletions net/mac80211/key.h
Expand Up @@ -165,6 +165,9 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
void ieee80211_free_sta_keys(struct ieee80211_local *local,
struct sta_info *sta);
void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata);
int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata,
unsigned long del_links_mask,
unsigned long add_links_mask);

#define key_mtx_dereference(local, ref) \
rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))
Expand Down
145 changes: 145 additions & 0 deletions net/mac80211/link.c
Expand Up @@ -9,6 +9,7 @@
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
#include "key.h"

void ieee80211_link_setup(struct ieee80211_link_data *link)
{
Expand Down Expand Up @@ -300,3 +301,147 @@ void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)

ieee80211_free_links(sdata, links);
}

static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
u16 active_links)
{
struct ieee80211_bss_conf *link_confs[IEEE80211_MLD_MAX_NUM_LINKS];
struct ieee80211_local *local = sdata->local;
u16 old_active = sdata->vif.active_links;
unsigned long rem = old_active & ~active_links;
unsigned long add = active_links & ~old_active;
struct sta_info *sta;
unsigned int link_id;
int ret, i;

if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL;

/* cannot activate links that don't exist */
if (active_links & ~sdata->vif.valid_links)
return -EINVAL;

/* nothing to do */
if (old_active == active_links)
return 0;

for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
link_confs[i] = sdata_dereference(sdata->vif.link_conf[i],
sdata);

if (add) {
sdata->vif.active_links |= active_links;
ret = drv_change_vif_links(local, sdata,
old_active,
sdata->vif.active_links,
link_confs);
if (ret) {
sdata->vif.active_links = old_active;
return ret;
}
}

for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_link_data *link;

link = sdata_dereference(sdata->link[link_id], sdata);

/* FIXME: kill TDLS connections on the link */

ieee80211_link_release_channel(link);
}

list_for_each_entry(sta, &local->sta_list, list) {
if (sdata != sta->sdata)
continue;
ret = drv_change_sta_links(local, sdata, &sta->sta,
old_active,
old_active | active_links);
WARN_ON(ret); // FIXME //
}

ret = ieee80211_key_switch_links(sdata, rem, add);
WARN_ON(ret); // FIXME //

list_for_each_entry(sta, &local->sta_list, list) {
if (sdata != sta->sdata)
continue;
ret = drv_change_sta_links(local, sdata, &sta->sta,
old_active | active_links,
active_links);
WARN_ON(ret); // FIXME //
}

for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_link_data *link;

link = sdata_dereference(sdata->link[link_id], sdata);

ret = ieee80211_link_use_channel(link, &link->conf->chandef,
IEEE80211_CHANCTX_SHARED);
WARN_ON(ret); // FIXME //

ieee80211_link_info_change_notify(sdata, link,
BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE |
BSS_CHANGED_ERP_SLOT |
BSS_CHANGED_HT |
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM |
BSS_CHANGED_QOS |
BSS_CHANGED_TXPOWER |
BSS_CHANGED_BANDWIDTH |
BSS_CHANGED_TWT |
BSS_CHANGED_HE_OBSS_PD |
BSS_CHANGED_HE_BSS_COLOR |
BSS_CHANGED_EHT_PUNCTURING);
ieee80211_mgd_set_link_qos_params(link);
}

old_active = sdata->vif.active_links;
sdata->vif.active_links = active_links;

if (rem) {
ret = drv_change_vif_links(local, sdata, old_active,
active_links, link_confs);
WARN_ON(ret); // FIXME //
}

return 0;
}

int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_local *local = sdata->local;
u16 old_active;
int ret;

sdata_lock(sdata);
mutex_lock(&local->sta_mtx);
mutex_lock(&local->mtx);
mutex_lock(&local->key_mtx);
old_active = sdata->vif.active_links;
if (old_active & active_links) {
/*
* if there's at least one link that stays active across
* the change then switch to it (to those) first, and
* then enable the additional links
*/
ret = _ieee80211_set_active_links(sdata,
old_active & active_links);
if (!ret)
ret = _ieee80211_set_active_links(sdata, active_links);
} else {
/* otherwise switch directly */
ret = _ieee80211_set_active_links(sdata, active_links);
}
mutex_unlock(&local->key_mtx);
mutex_unlock(&local->mtx);
mutex_unlock(&local->sta_mtx);
sdata_unlock(sdata);

return ret;
}
EXPORT_SYMBOL_GPL(ieee80211_set_active_links);

0 comments on commit 43af0cd

Please sign in to comment.