summaryrefslogtreecommitdiffstats
path: root/net/mac80211/tdls.c
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2015-07-08 14:41:45 +0200
committerJohannes Berg <johannes.berg@intel.com>2015-07-17 15:40:15 +0200
commitc8ff71e667d9fcf775e8b8bbd568d32d48cfb864 (patch)
tree0b2815a3d230575c1ce9dcf6419119124d30993f /net/mac80211/tdls.c
parentMerge branch 'mac80211' into mac80211-next (diff)
downloadlinux-c8ff71e667d9fcf775e8b8bbd568d32d48cfb864.tar.xz
linux-c8ff71e667d9fcf775e8b8bbd568d32d48cfb864.zip
mac80211: TDLS: handle chan-switch in RTNL locked work
Move TDLS channel-switch Rx handling into an RTNL locked work. This is required to add proper regulatory checking to incoming channel-switch requests. Queue incoming requests in a dedicated skb queue and handle the request in a device-specific work to avoid deadlocking on interface removal. Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/tdls.c')
-rw-r--r--net/mac80211/tdls.c34
1 files changed, 32 insertions, 2 deletions
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 20c9dbde3b2b..91e86bf76867 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -12,6 +12,7 @@
#include <linux/ieee80211.h>
#include <linux/log2.h>
#include <net/cfg80211.h>
+#include <linux/rtnetlink.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@@ -1800,12 +1801,15 @@ out:
return ret;
}
-void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb)
+static void
+ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
{
struct ieee80211_tdls_data *tf = (void *)skb->data;
struct wiphy *wiphy = sdata->local->hw.wiphy;
+ ASSERT_RTNL();
+
/* make sure the driver supports it */
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
return;
@@ -1847,3 +1851,29 @@ void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
}
rcu_read_unlock();
}
+
+void ieee80211_tdls_chsw_work(struct work_struct *wk)
+{
+ struct ieee80211_local *local =
+ container_of(wk, struct ieee80211_local, tdls_chsw_work);
+ struct ieee80211_sub_if_data *sdata;
+ struct sk_buff *skb;
+ struct ieee80211_tdls_data *tf;
+
+ rtnl_lock();
+ while ((skb = skb_dequeue(&local->skb_queue_tdls_chsw))) {
+ tf = (struct ieee80211_tdls_data *)skb->data;
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata) ||
+ sdata->vif.type != NL80211_IFTYPE_STATION ||
+ !ether_addr_equal(tf->da, sdata->vif.addr))
+ continue;
+
+ ieee80211_process_tdls_channel_switch(sdata, skb);
+ break;
+ }
+
+ kfree_skb(skb);
+ }
+ rtnl_unlock();
+}