diff options
author | Arik Nemtsov <arik@wizery.com> | 2015-07-08 15:41:45 +0300 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2015-07-17 16:40:15 +0300 |
commit | c8ff71e667d9fcf775e8b8bbd568d32d48cfb864 (patch) | |
tree | 0b2815a3d230575c1ce9dcf6419119124d30993f /net/mac80211/tdls.c | |
parent | 72bbe3d1c2d7a9e257956912040ffc7ba9f71e0d (diff) | |
download | linux-c8ff71e667d9fcf775e8b8bbd568d32d48cfb864.tar.xz |
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.c | 34 |
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(); +} |