wifi: mac80211: protect AP template pointers with a spinlock

The AP's beacon, presp etc. templates are only updated under
wiphy mutex right now. Prepare for being able to update them
without the wiphy mutex by using a (per-link) spinlock for
them.

Link: https://patch.msgid.link/20261009201417.f9414c150e5d.Iba8a15c4d37e441642b6df68c33ef8c97340e9a9@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-10-09 20:10:01 +02:00
parent 63892371b2
commit e70df36f36
3 changed files with 42 additions and 43 deletions

View File

@ -1123,13 +1123,11 @@ ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_color_change_settings *cca,
struct ieee80211_link_data *link)
{
struct probe_resp *new, *old;
struct probe_resp *new;
if (!resp || !resp_len)
return 1;
old = sdata_dereference(link->u.ap.probe_resp, sdata);
new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
if (!new)
return -ENOMEM;
@ -1144,9 +1142,7 @@ ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
else if (cca)
new->cntdwn_counter_offsets[0] = cca->counter_offset_presp;
rcu_assign_pointer(link->u.ap.probe_resp, new);
if (old)
kfree_rcu(old, rcu_head);
ap_tmpl_replace(link, link->u.ap.probe_resp, new);
return 0;
}
@ -1157,7 +1153,7 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *link_conf,
u64 *changed)
{
struct fils_discovery_data *new, *old = NULL;
struct fils_discovery_data *new = NULL;
struct ieee80211_fils_discovery *fd;
/*
@ -1175,20 +1171,15 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
fd->min_interval = params->min_interval;
fd->max_interval = params->max_interval;
old = sdata_dereference(link->u.ap.fils_discovery, sdata);
if (params->tmpl && params->tmpl_len) {
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
if (!new)
return -ENOMEM;
new->len = params->tmpl_len;
memcpy(new->data, params->tmpl, params->tmpl_len);
rcu_assign_pointer(link->u.ap.fils_discovery, new);
} else {
RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
}
if (old)
kfree_rcu(old, rcu_head);
ap_tmpl_replace(link, link->u.ap.fils_discovery, new);
*changed |= BSS_CHANGED_FILS_DISCOVERY;
return 0;
@ -1201,7 +1192,7 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *link_conf,
u64 *changed)
{
struct unsol_bcast_probe_resp_data *new, *old = NULL;
struct unsol_bcast_probe_resp_data *new = NULL;
/*
* This configuration is only applicable to transmitting BSSes.
@ -1216,21 +1207,15 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
link_conf->unsol_bcast_probe_resp_interval = params->interval;
old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
if (params->tmpl && params->tmpl_len) {
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
if (!new)
return -ENOMEM;
new->len = params->tmpl_len;
memcpy(new->data, params->tmpl, params->tmpl_len);
rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new);
} else {
RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
}
if (old)
kfree_rcu(old, rcu_head);
ap_tmpl_replace(link, link->u.ap.unsol_bcast_probe_resp, new);
*changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
return 0;
@ -1649,12 +1634,9 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
_changed |= BSS_CHANGED_FTM_RESPONDER;
}
rcu_assign_pointer(link->u.ap.beacon, new);
ap_tmpl_replace(link, link->u.ap.beacon, new);
sdata->u.ap.active = true;
if (old)
kfree_rcu(old, rcu_head);
ieee80211_update_ap_bandwidth(link, params);
*changed |= _changed;
@ -1956,11 +1938,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
err = drv_start_ap(sdata->local, sdata, link_conf);
if (err) {
old = sdata_dereference(link->u.ap.beacon, sdata);
if (old)
kfree_rcu(old, rcu_head);
RCU_INIT_POINTER(link->u.ap.beacon, NULL);
ap_tmpl_replace(link, link->u.ap.beacon, NULL);
if (ieee80211_num_beaconing_links(sdata) == 0)
sdata->u.ap.active = false;
@ -2088,16 +2066,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
lockdep_assert_wiphy(local->hw.wiphy);
old_beacon = sdata_dereference(link->u.ap.beacon, sdata);
if (!old_beacon)
if (!sdata_dereference(link->u.ap.beacon, sdata))
return -ENOENT;
old_probe_resp = sdata_dereference(link->u.ap.probe_resp,
sdata);
old_fils_discovery = sdata_dereference(link->u.ap.fils_discovery,
sdata);
old_unsol_bcast_probe_resp =
sdata_dereference(link->u.ap.unsol_bcast_probe_resp,
sdata);
old_s1g_short_beacon =
sdata_dereference(link->u.ap.s1g_short_beacon, sdata);
@ -2118,10 +2088,20 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
}
/* remove beacon and probe response */
RCU_INIT_POINTER(link->u.ap.beacon, NULL);
RCU_INIT_POINTER(link->u.ap.probe_resp, NULL);
RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
scoped_guard(spinlock, &link->ap_tmpl_lock) {
old_beacon = ap_tmpl_dereference(link, link->u.ap.beacon);
old_probe_resp = ap_tmpl_dereference(link,
link->u.ap.probe_resp);
old_fils_discovery =
ap_tmpl_dereference(link, link->u.ap.fils_discovery);
old_unsol_bcast_probe_resp =
ap_tmpl_dereference(link,
link->u.ap.unsol_bcast_probe_resp);
RCU_INIT_POINTER(link->u.ap.beacon, NULL);
RCU_INIT_POINTER(link->u.ap.probe_resp, NULL);
RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
}
RCU_INIT_POINTER(link->u.ap.s1g_short_beacon, NULL);
kfree_rcu(old_beacon, rcu_head);
if (old_probe_resp)

View File

@ -1152,6 +1152,9 @@ struct ieee80211_link_data {
struct ieee80211_link_data_ap ap;
} u;
/* protects replacing the u.ap template pointers, also without wiphy mutex */
spinlock_t ap_tmpl_lock;
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
struct ieee80211_bss_conf *conf;
@ -1166,6 +1169,21 @@ struct ieee80211_link_data {
#endif
};
#define ap_tmpl_dereference(link, p) \
rcu_dereference_protected(p, lockdep_is_held(&(link)->ap_tmpl_lock))
#define ap_tmpl_replace(link, p, new) \
do { \
typeof(ap_tmpl_dereference(link, p)) __old; \
\
spin_lock(&(link)->ap_tmpl_lock); \
__old = rcu_replace_pointer(p, new, \
lockdep_is_held(&(link)->ap_tmpl_lock));\
spin_unlock(&(link)->ap_tmpl_lock); \
if (__old) \
kfree_rcu(__old, rcu_head); \
} while (0)
struct ieee80211_sub_if_data {
struct list_head list;

View File

@ -126,6 +126,7 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
link->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
link->user_power_level = sdata->local->user_power_level;
link_conf->txpower = INT_MIN;
spin_lock_init(&link->ap_tmpl_lock);
wiphy_work_init(&link->csa.finalize_work,
ieee80211_csa_finalize_work);