summaryrefslogtreecommitdiff
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c251
1 files changed, 207 insertions, 44 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index fb8d102fca48..d5e904bff624 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -51,7 +51,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
int power;
rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
if (!chanctx_conf) {
rcu_read_unlock();
return false;
@@ -60,11 +60,11 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock();
- if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
- power = min(power, sdata->user_power_level);
+ if (sdata->deflink.user_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->deflink.user_power_level);
- if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
- power = min(power, sdata->ap_power_level);
+ if (sdata->deflink.ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->deflink.ap_power_level);
if (power != sdata->vif.bss_conf.txpower) {
sdata->vif.bss_conf.txpower = power;
@@ -80,7 +80,8 @@ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
{
if (__ieee80211_recalc_txpower(sdata) ||
(update_bss && ieee80211_sdata_running(sdata)))
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
+ ieee80211_link_info_change_notify(sdata, 0,
+ BSS_CHANGED_TXPOWER);
}
static u32 __ieee80211_idle_off(struct ieee80211_local *local)
@@ -219,8 +220,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
ret = eth_mac_addr(dev, sa);
- if (ret == 0)
+ if (ret == 0) {
memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
+ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
+ }
return ret;
}
@@ -275,7 +278,7 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
* will not add another interface while any channel
* switch is active.
*/
- if (nsdata->vif.csa_active)
+ if (nsdata->vif.bss_conf.csa_active)
return -EBUSY;
/*
@@ -377,7 +380,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
bool cancel_scan;
struct cfg80211_nan_func *func;
+ spin_lock_bh(&local->fq.lock);
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+ spin_unlock_bh(&local->fq.lock);
cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
if (cancel_scan)
@@ -448,29 +453,34 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
cancel_work_sync(&local->dynamic_ps_enable_work);
cancel_work_sync(&sdata->recalc_smps);
+
sdata_lock(sdata);
+ WARN(sdata->vif.valid_links,
+ "destroying interface with valid links 0x%04x\n",
+ sdata->vif.valid_links);
+
mutex_lock(&local->mtx);
- sdata->vif.csa_active = false;
+ sdata->vif.bss_conf.csa_active = false;
if (sdata->vif.type == NL80211_IFTYPE_STATION)
- sdata->u.mgd.csa_waiting_bcn = false;
- if (sdata->csa_block_tx) {
+ sdata->deflink.u.mgd.csa_waiting_bcn = false;
+ if (sdata->deflink.csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
- sdata->csa_block_tx = false;
+ sdata->deflink.csa_block_tx = false;
}
mutex_unlock(&local->mtx);
sdata_unlock(sdata);
- cancel_work_sync(&sdata->csa_finalize_work);
- cancel_work_sync(&sdata->color_change_finalize_work);
+ cancel_work_sync(&sdata->deflink.csa_finalize_work);
+ cancel_work_sync(&sdata->deflink.color_change_finalize_work);
- cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+ cancel_delayed_work_sync(&sdata->deflink.dfs_cac_timer_work);
if (sdata->wdev.cac_started) {
chandef = sdata->vif.bss_conf.chandef;
WARN_ON(local->suspended);
mutex_lock(&local->mtx);
- ieee80211_vif_release_channel(sdata);
+ ieee80211_link_release_channel(sdata->link[0]);
mutex_unlock(&local->mtx);
cfg80211_cac_event(sdata->dev, &chandef,
NL80211_RADAR_CAC_ABORTED,
@@ -502,7 +512,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
mutex_lock(&local->mtx);
list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
- RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+ RCU_INIT_POINTER(sdata->vif.bss_conf.chanctx_conf, NULL);
/* see comment in the default case below */
ieee80211_free_keys(sdata, true);
/* no need to tell driver */
@@ -831,7 +841,7 @@ static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
}
}
- sta = sta_info_get(sdata, sdata->u.mgd.bssid);
+ sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
break;
default:
goto out;
@@ -1011,6 +1021,65 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
}
+static void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
+ int link_id,
+ struct ieee80211_link_data *link,
+ struct ieee80211_bss_conf *link_conf)
+{
+ bool deflink = link_id < 0;
+
+ if (link_id < 0)
+ link_id = 0;
+
+ sdata->vif.link_conf[link_id] = link_conf;
+ sdata->link[link_id] = link;
+
+ link->sdata = sdata;
+ link->link_id = link_id;
+
+ INIT_WORK(&link->csa_finalize_work,
+ ieee80211_csa_finalize_work);
+ INIT_WORK(&link->color_change_finalize_work,
+ ieee80211_color_change_finalize_work);
+ INIT_LIST_HEAD(&link->assigned_chanctx_list);
+ INIT_LIST_HEAD(&link->reserved_chanctx_list);
+ INIT_DELAYED_WORK(&link->dfs_cac_timer_work,
+ ieee80211_dfs_cac_timer_work);
+
+ if (!deflink) {
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP:
+ ether_addr_copy(link_conf->addr,
+ sdata->wdev.links[link_id].addr);
+ WARN_ON(!(sdata->wdev.valid_links & BIT(link_id)));
+ break;
+ case NL80211_IFTYPE_STATION:
+ eth_random_addr(link_conf->addr);
+ ether_addr_copy(sdata->wdev.links[link_id].addr,
+ link_conf->addr);
+ break;
+ default:
+ WARN_ON(1);
+ }
+ }
+}
+
+static void ieee80211_sdata_init(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ sdata->local = local;
+
+ /*
+ * Initialize the default link, so we can use link_id 0 for non-MLD,
+ * and that continues to work for non-MLD-aware drivers that use just
+ * vif.bss_conf instead of vif.link_conf.
+ *
+ * Note that we never change this, so if link ID 0 isn't used in an
+ * MLD connection, we get a separate allocation for it.
+ */
+ ieee80211_link_init(sdata, -1, &sdata->deflink, &sdata->vif.bss_conf);
+}
+
int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
@@ -1030,12 +1099,13 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
return -ENOMEM;
/* set up data */
- sdata->local = local;
sdata->vif.type = NL80211_IFTYPE_MONITOR;
snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
wiphy_name(local->hw.wiphy));
sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
+ ieee80211_sdata_init(local, sdata);
+
ieee80211_set_default_queues(sdata);
ret = drv_add_interface(local, sdata);
@@ -1058,8 +1128,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
mutex_unlock(&local->iflist_mtx);
mutex_lock(&local->mtx);
- ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
- IEEE80211_CHANCTX_EXCLUSIVE);
+ ret = ieee80211_link_use_channel(sdata->link[0], &local->monitor_chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
mutex_unlock(&local->mtx);
if (ret) {
mutex_lock(&local->iflist_mtx);
@@ -1103,7 +1173,7 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
synchronize_net();
mutex_lock(&local->mtx);
- ieee80211_vif_release_channel(sdata);
+ ieee80211_link_release_channel(sdata->link[0]);
mutex_unlock(&local->mtx);
drv_remove_interface(local, sdata);
@@ -1208,8 +1278,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
/* no need to tell driver, but set carrier and chanctx */
- if (rtnl_dereference(sdata->bss->beacon)) {
- ieee80211_vif_vlan_copy_chanctx(sdata);
+ if (sdata->bss->active) {
+ ieee80211_link_vlan_copy_chanctx(sdata->link[0]);
netif_carrier_on(dev);
ieee80211_set_vif_encap_ops(sdata);
} else {
@@ -1281,7 +1351,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
sdata->vif.type != NL80211_IFTYPE_NAN)
changed |= ieee80211_reset_erp_info(sdata);
- ieee80211_bss_info_change_notify(sdata, changed);
+ ieee80211_link_info_change_notify(sdata, 0, changed);
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
@@ -1457,14 +1527,15 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta)
- ieee80211_vht_handle_opmode(sdata, sta, opmode,
- band);
+ ieee80211_vht_handle_opmode(sdata,
+ &sta->deflink,
+ opmode, band);
mutex_unlock(&local->sta_mtx);
break;
}
case WLAN_VHT_ACTION_GROUPID_MGMT:
- ieee80211_process_mu_groups(sdata, mgmt);
+ ieee80211_process_mu_groups(sdata, 0, mgmt);
break;
default:
WARN_ON(1);
@@ -1618,7 +1689,7 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data, recalc_smps);
- ieee80211_recalc_smps(sdata);
+ ieee80211_recalc_smps(sdata, 0);
}
/*
@@ -1630,8 +1701,9 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
0xff, 0xff, 0xff};
- /* clear type-dependent union */
+ /* clear type-dependent unions */
memset(&sdata->u, 0, sizeof(sdata->u));
+ memset(&sdata->deflink.u, 0, sizeof(sdata->deflink.u));
/* and set some type-dependent values */
sdata->vif.type = type;
@@ -1642,7 +1714,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->control_port_no_encrypt = false;
sdata->control_port_over_nl80211 = false;
sdata->control_port_no_preauth = false;
- sdata->vif.bss_conf.idle = true;
+ sdata->vif.cfg.idle = true;
sdata->vif.bss_conf.txpower = INT_MIN; /* unset */
sdata->noack_map = 0;
@@ -1657,10 +1729,6 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sdata->status_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
- INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
- INIT_WORK(&sdata->color_change_finalize_work, ieee80211_color_change_finalize_work);
- INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
- INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
@@ -1679,7 +1747,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.p2p = true;
fallthrough;
case NL80211_IFTYPE_STATION:
- sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
+ sdata->vif.bss_conf.bssid = sdata->deflink.u.mgd.bssid;
ieee80211_sta_setup_sdata(sdata);
break;
case NL80211_IFTYPE_OCB:
@@ -1732,6 +1800,10 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
if (!local->ops->change_interface)
return -EBUSY;
+ /* for now, don't support changing while links exist */
+ if (sdata->vif.valid_links)
+ return -EBUSY;
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
if (!list_empty(&sdata->u.ap.vlans))
@@ -1994,6 +2066,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
strlcpy(sdata->name, name, IFNAMSIZ);
ieee80211_assign_perm_addr(local, wdev->address, type);
memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
+ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
} else {
int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
sizeof(void *));
@@ -2058,6 +2131,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sdata = netdev_priv(ndev);
ndev->ieee80211_ptr = &sdata->wdev;
memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
+ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
memcpy(sdata->name, ndev->name, IFNAMSIZ);
if (txq_size) {
@@ -2070,14 +2144,13 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
/* initialise type-independent data */
sdata->wdev.wiphy = local->hw.wiphy;
- sdata->local = local;
+
+ ieee80211_sdata_init(local, sdata);
ieee80211_init_frag_cache(&sdata->frags);
INIT_LIST_HEAD(&sdata->key_list);
- INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
- ieee80211_dfs_cac_timer_work);
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
ieee80211_delayed_tailroom_dec);
@@ -2105,13 +2178,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
}
}
- for (i = 0; i < IEEE80211_NUM_ACS; i++)
- init_airtime_info(&sdata->airtime[i], &local->airtime[i]);
-
ieee80211_set_default_queues(sdata);
- sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
- sdata->user_power_level = local->user_power_level;
+ sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ sdata->deflink.user_power_level = local->user_power_level;
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
@@ -2282,3 +2352,96 @@ void ieee80211_vif_dec_num_mcast(struct ieee80211_sub_if_data *sdata)
else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
atomic_dec(&sdata->u.vlan.num_mcast_sta);
}
+
+int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
+ u16 new_links)
+{
+ u16 old_links = sdata->vif.valid_links;
+ unsigned long add = new_links & ~old_links;
+ unsigned long rem = old_links & ~new_links;
+ unsigned int link_id;
+ int ret;
+ struct {
+ struct ieee80211_link_data data;
+ struct ieee80211_bss_conf conf;
+ } *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link;
+ struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS];
+ bool use_deflink = old_links == 0; /* set for error case */
+
+ sdata_assert_lock(sdata);
+
+ if (old_links == new_links)
+ return 0;
+
+ /* allocate new link structures first */
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (!link) {
+ ret = -ENOMEM;
+ goto free;
+ }
+ links[link_id] = link;
+ }
+
+ /* keep track of the old pointers for the driver */
+ BUILD_BUG_ON(sizeof(old) != sizeof(sdata->vif.link_conf));
+ memcpy(old, sdata->vif.link_conf, sizeof(old));
+ /* and for us in error cases */
+ BUILD_BUG_ON(sizeof(old_data) != sizeof(sdata->link));
+ memcpy(old_data, sdata->link, sizeof(old_data));
+
+ /* link them into data structures */
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ WARN_ON(!use_deflink &&
+ sdata->link[link_id] == &sdata->deflink);
+
+ link = links[link_id];
+ ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
+ }
+
+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ sdata->link[link_id] = NULL;
+ sdata->vif.link_conf[link_id] = NULL;
+ }
+
+ sdata->vif.valid_links = new_links;
+
+ /* tell the driver */
+ ret = drv_change_vif_links(sdata->local, sdata,
+ old_links, new_links,
+ old);
+ if (ret) {
+ /* restore config */
+ memcpy(sdata->link, old_data, sizeof(old_data));
+ memcpy(sdata->vif.link_conf, old, sizeof(old));
+ sdata->vif.valid_links = old_links;
+ /* and free the newly allocated links */
+ goto free;
+ }
+
+ /* use deflink/bss_conf again if and only if there are no more links */
+ use_deflink = new_links == 0;
+
+ /* now use this to free the old links */
+ memset(links, 0, sizeof(links));
+ for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+ if (sdata->link[link_id] == &sdata->deflink)
+ continue;
+ /*
+ * we must have allocated the data through this path so
+ * we know we can free both at the same time
+ */
+ links[link_id] = container_of(sdata->link[link_id],
+ typeof(*links[link_id]),
+ data);
+ }
+
+free:
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)
+ kfree(links[link_id]);
+ if (use_deflink)
+ ieee80211_link_init(sdata, -1, &sdata->deflink,
+ &sdata->vif.bss_conf);
+ return ret;
+}