aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Kazior <michal.kazior@tieto.com>2014-03-05 13:14:08 +0100
committerLuciano Coelho <luciano.coelho@intel.com>2014-03-24 10:18:30 +0200
commit230dd6171e96d881ba14b31d587e5cb06ec87192 (patch)
tree8ebf015969ce66a6bfe303512e4b884c650012a3
parent400f6bc852b92e85bb6bac72436d11647125da5d (diff)
downloadmac80211-next-csa-230dd6171e96d881ba14b31d587e5cb06ec87192.tar.gz
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: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
-rw-r--r--net/mac80211/cfg.c5
-rw-r--r--net/mac80211/chan.c2
-rw-r--r--net/mac80211/ieee80211_i.h4
-rw-r--r--net/mac80211/iface.c9
4 files changed, 14 insertions, 6 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index fae76f79e2f8fb..137d3793398a1a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -983,10 +983,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
mutex_lock(&local->mtx);
err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED);
+ if (!err)
+ ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
mutex_unlock(&local->mtx);
if (err)
return err;
- ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
/*
* Apply control port protocol, this allows us to
@@ -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);
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
- ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
mutex_lock(&local->mtx);
+ ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
ieee80211_vif_release_channel(sdata);
mutex_unlock(&local->mtx);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 384f3c72bbbee5..1ae6ba30c105d1 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -706,7 +706,7 @@ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sub_if_data *vlan;
struct ieee80211_chanctx_conf *conf;
- ASSERT_RTNL();
+ lockdep_assert_held(&local->mtx);
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
return;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 54bc43e0c1551e..e2103b4d8df5be 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -260,7 +260,7 @@ struct ieee80211_if_ap {
/* to be used after channel switch. */
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;
atomic_t num_mcast_sta; /* number of stations receiving multicast */
@@ -276,7 +276,7 @@ struct ieee80211_if_wds {
};
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 */
struct sta_info __rcu *sta;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 4c554818871d58..e753d722dcea63 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -496,7 +496,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
if (!sdata->bss)
return -ENOLINK;
+ mutex_lock(&local->mtx);
list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
+ mutex_unlock(&local->mtx);
master = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap);
@@ -726,8 +728,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
drv_stop(local);
err_del_bss:
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);
+ mutex_unlock(&local->mtx);
+ }
/* might already be clear but that doesn't matter */
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
return res;
@@ -879,7 +884,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
+ mutex_lock(&local->mtx);
list_del(&sdata->u.vlan.list);
+ mutex_unlock(&local->mtx);
rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
/* no need to tell driver */
break;