Commit 4e141dad authored by Michal Kazior's avatar Michal Kazior Committed by Johannes Berg

mac80211: protect AP VLAN list with local->mtx

It was impossible to change chanctx of master AP
for AP VLANs because the copy function requires
RTNL which can't be simply taken in mac80211 code
due to possible deadlocks.

This is required for future chanctx reservation
that re-bind vifs to new chanctx. This requires
safe AP VLAN iteration without RTNL.

Now VLANs can be iterated while holding either
RTNL or local->mtx because the list is modified
while holding both of these locks.
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarLuciano Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent b6a55015
...@@ -983,10 +983,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, ...@@ -983,10 +983,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
err = ieee80211_vif_use_channel(sdata, &params->chandef, err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED); IEEE80211_CHANCTX_SHARED);
if (!err)
ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
if (err) if (err)
return err; return err;
ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
/* /*
* Apply control port protocol, this allows us to * Apply control port protocol, this allows us to
...@@ -1139,8 +1140,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) ...@@ -1139,8 +1140,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
skb_queue_purge(&sdata->u.ap.ps.bc_buf); skb_queue_purge(&sdata->u.ap.ps.bc_buf);
ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
ieee80211_vif_release_channel(sdata); ieee80211_vif_release_channel(sdata);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
......
...@@ -712,7 +712,7 @@ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, ...@@ -712,7 +712,7 @@ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sub_if_data *vlan; struct ieee80211_sub_if_data *vlan;
struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx_conf *conf;
ASSERT_RTNL(); lockdep_assert_held(&local->mtx);
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
return; return;
......
...@@ -260,7 +260,7 @@ struct ieee80211_if_ap { ...@@ -260,7 +260,7 @@ struct ieee80211_if_ap {
/* to be used after channel switch. */ /* to be used after channel switch. */
struct cfg80211_beacon_data *next_beacon; struct cfg80211_beacon_data *next_beacon;
struct list_head vlans; struct list_head vlans; /* write-protected with RTNL and local->mtx */
struct ps_data ps; struct ps_data ps;
atomic_t num_mcast_sta; /* number of stations receiving multicast */ atomic_t num_mcast_sta; /* number of stations receiving multicast */
...@@ -276,7 +276,7 @@ struct ieee80211_if_wds { ...@@ -276,7 +276,7 @@ struct ieee80211_if_wds {
}; };
struct ieee80211_if_vlan { struct ieee80211_if_vlan {
struct list_head list; struct list_head list; /* write-protected with RTNL and local->mtx */
/* used for all tx if the VLAN is configured to 4-addr mode */ /* used for all tx if the VLAN is configured to 4-addr mode */
struct sta_info __rcu *sta; struct sta_info __rcu *sta;
......
...@@ -496,7 +496,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) ...@@ -496,7 +496,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
if (!sdata->bss) if (!sdata->bss)
return -ENOLINK; return -ENOLINK;
mutex_lock(&local->mtx);
list_add(&sdata->u.vlan.list, &sdata->bss->vlans); list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
mutex_unlock(&local->mtx);
master = container_of(sdata->bss, master = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap); struct ieee80211_sub_if_data, u.ap);
...@@ -726,8 +728,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) ...@@ -726,8 +728,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
drv_stop(local); drv_stop(local);
err_del_bss: err_del_bss:
sdata->bss = NULL; sdata->bss = NULL;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
mutex_lock(&local->mtx);
list_del(&sdata->u.vlan.list); list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
}
/* might already be clear but that doesn't matter */ /* might already be clear but that doesn't matter */
clear_bit(SDATA_STATE_RUNNING, &sdata->state); clear_bit(SDATA_STATE_RUNNING, &sdata->state);
return res; return res;
...@@ -879,7 +884,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -879,7 +884,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
mutex_lock(&local->mtx);
list_del(&sdata->u.vlan.list); list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL); RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
/* no need to tell driver */ /* no need to tell driver */
break; break;
......
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