Commit d8787ec6 authored by Johannes Berg's avatar Johannes Berg

wifi: mac80211: add vif link addition/removal

Add the necessary infrastructure, including a new driver
method, to add/remove links to/from an interface.

Also add the missing link address to bss_conf (which we
use as link_conf too), and fill it, in station mode for
now just randomly, in AP mode we get the address from
cfg80211 since the link must be created with an address
first.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent d648c230
......@@ -515,6 +515,7 @@ struct ieee80211_fils_discovery {
* This structure keeps information about a BSS (and an association
* to that BSS) that can change during the lifetime of the BSS.
*
* @addr: (link) address used locally
* @htc_trig_based_pkt_ext: default PE in 4us units, if BSS supports HE
* @uora_exists: is the UORA element advertised by AP
* @ack_enabled: indicates support to receive a multi-TID that solicits either
......@@ -638,6 +639,7 @@ struct ieee80211_fils_discovery {
*/
struct ieee80211_bss_conf {
const u8 *bssid;
u8 addr[ETH_ALEN] __aligned(2);
u8 htc_trig_based_pkt_ext;
bool uora_exists;
u8 uora_ocw_range;
......@@ -1743,6 +1745,7 @@ struct ieee80211_vif_cfg {
* or the BSS we're associated to
* @link_conf: in case of MLD, the per-link BSS configuration,
* indexed by link ID
* @valid_links: bitmap of valid links, or 0 for non-MLO.
* @addr: address of this interface
* @p2p: indicates whether this AP or STA interface is a p2p
* interface, i.e. a GO or p2p-sta respectively
......@@ -1778,6 +1781,7 @@ struct ieee80211_vif {
struct ieee80211_vif_cfg cfg;
struct ieee80211_bss_conf bss_conf;
struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
u16 valid_links;
u8 addr[ETH_ALEN] __aligned(2);
bool p2p;
......@@ -4028,6 +4032,13 @@ struct ieee80211_prep_tx_info {
* disable background CAC/radar detection.
* @net_fill_forward_path: Called from .ndo_fill_forward_path in order to
* resolve a path for hardware flow offloading
* @change_vif_links: Change the valid links on an interface, note that while
* removing the old link information is still valid (link_conf pointer),
* but may immediately disappear after the function returns. The old or
* new links bitmaps may be 0 if going from/to a non-MLO situation.
* The @old[] array contains pointers to the old bss_conf structures
* that were already removed, in case they're needed.
* This callback can sleep.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
......@@ -4371,6 +4382,10 @@ struct ieee80211_ops {
struct ieee80211_sta *sta,
struct net_device_path_ctx *ctx,
struct net_device_path *path);
int (*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]);
};
/**
......
......@@ -1533,4 +1533,25 @@ static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
return ret;
}
static inline int drv_change_vif_links(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 old_links, u16 new_links,
struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
{
int ret = -EOPNOTSUPP;
might_sleep();
if (!check_sdata_in_driver(sdata))
return -EIO;
trace_drv_change_vif_links(local, sdata, old_links, new_links);
if (local->ops->change_vif_links)
ret = local->ops->change_vif_links(&local->hw, &sdata->vif,
old_links, new_links, old);
trace_drv_return_int(local, ret);
return ret;
}
#endif /* __MAC80211_DRIVER_OPS */
......@@ -2006,6 +2006,9 @@ void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
u16 new_links);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
bool update_bss);
......
......@@ -220,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;
}
......@@ -449,7 +451,12 @@ 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.bss_conf.csa_active = false;
if (sdata->vif.type == NL80211_IFTYPE_STATION)
......@@ -1013,10 +1020,15 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
}
static void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
unsigned int link_id,
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;
......@@ -1031,6 +1043,23 @@ static void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
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,
......@@ -1046,7 +1075,7 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
* 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, 0, &sdata->deflink, &sdata->vif.bss_conf);
ieee80211_link_init(sdata, -1, &sdata->deflink, &sdata->vif.bss_conf);
}
int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
......@@ -1768,6 +1797,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))
......@@ -2030,6 +2063,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 *));
......@@ -2094,6 +2128,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) {
......@@ -2318,3 +2353,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;
}
......@@ -2455,6 +2455,33 @@ DEFINE_EVENT(sta_event, drv_net_fill_forward_path,
TP_ARGS(local, sdata, sta)
);
TRACE_EVENT(drv_change_vif_links,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 old_links, u16 new_links),
TP_ARGS(local, sdata, old_links, new_links),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u16, old_links)
__field(u16, new_links)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->old_links = old_links;
__entry->new_links = new_links;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT " old_links:0x%04x, new_links:0x%04x\n",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->old_links, __entry->new_links
)
);
/*
* Tracing for API calls that drivers call.
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment