提交 c8ff71e6 编写于 作者: A Arik Nemtsov 提交者: Johannes Berg

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: NArik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: NEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: NJohannes Berg <johannes.berg@intel.com>
上级 72bbe3d1
...@@ -1008,7 +1008,6 @@ enum sdata_queue_type { ...@@ -1008,7 +1008,6 @@ enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_AGG_STOP = 2, IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
}; };
enum { enum {
...@@ -1351,6 +1350,10 @@ struct ieee80211_local { ...@@ -1351,6 +1350,10 @@ struct ieee80211_local {
/* extended capabilities provided by mac80211 */ /* extended capabilities provided by mac80211 */
u8 ext_capa[8]; u8 ext_capa[8];
/* TDLS channel switch */
struct work_struct tdls_chsw_work;
struct sk_buff_head skb_queue_tdls_chsw;
}; };
static inline struct ieee80211_sub_if_data * static inline struct ieee80211_sub_if_data *
...@@ -2054,9 +2057,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, ...@@ -2054,9 +2057,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
const u8 *addr); const u8 *addr);
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata); void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
void ieee80211_tdls_chsw_work(struct work_struct *wk);
extern const struct ethtool_ops ieee80211_ethtool_ops; extern const struct ethtool_ops ieee80211_ethtool_ops;
......
...@@ -1242,8 +1242,6 @@ static void ieee80211_iface_work(struct work_struct *work) ...@@ -1242,8 +1242,6 @@ static void ieee80211_iface_work(struct work_struct *work)
WLAN_BACK_RECIPIENT, 0, WLAN_BACK_RECIPIENT, 0,
false); false);
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
ieee80211_process_tdls_channel_switch(sdata, skb);
} else if (ieee80211_is_action(mgmt->frame_control) && } else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) { mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len; int len = skb->len;
......
...@@ -629,6 +629,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, ...@@ -629,6 +629,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
INIT_WORK(&local->sched_scan_stopped_work, INIT_WORK(&local->sched_scan_stopped_work,
ieee80211_sched_scan_stopped_work); ieee80211_sched_scan_stopped_work);
INIT_WORK(&local->tdls_chsw_work, ieee80211_tdls_chsw_work);
spin_lock_init(&local->ack_status_lock); spin_lock_init(&local->ack_status_lock);
idr_init(&local->ack_status_frames); idr_init(&local->ack_status_frames);
...@@ -645,6 +647,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, ...@@ -645,6 +647,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable); skb_queue_head_init(&local->skb_queue_unreliable);
skb_queue_head_init(&local->skb_queue_tdls_chsw);
ieee80211_alloc_led_names(local); ieee80211_alloc_led_names(local);
...@@ -1161,6 +1164,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) ...@@ -1161,6 +1164,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
cancel_work_sync(&local->restart_work); cancel_work_sync(&local->restart_work);
cancel_work_sync(&local->reconfig_filter); cancel_work_sync(&local->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);
flush_work(&local->sched_scan_stopped_work); flush_work(&local->sched_scan_stopped_work);
ieee80211_clear_tx_pending(local); ieee80211_clear_tx_pending(local);
...@@ -1171,6 +1175,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) ...@@ -1171,6 +1175,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n"); wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable); skb_queue_purge(&local->skb_queue_unreliable);
skb_queue_purge(&local->skb_queue_tdls_chsw);
destroy_workqueue(local->workqueue); destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy); wiphy_unregister(local->hw.wiphy);
......
...@@ -2410,9 +2410,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) ...@@ -2410,9 +2410,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
tf->category == WLAN_CATEGORY_TDLS && tf->category == WLAN_CATEGORY_TDLS &&
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST || (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) { tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW; skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
skb_queue_tail(&sdata->skb_queue, rx->skb); schedule_work(&local->tdls_chsw_work);
ieee80211_queue_work(&rx->local->hw, &sdata->work);
if (rx->sta) if (rx->sta)
rx->sta->rx_packets++; rx->sta->rx_packets++;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <linux/rtnetlink.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "driver-ops.h" #include "driver-ops.h"
...@@ -1800,12 +1801,15 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, ...@@ -1800,12 +1801,15 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
return ret; return ret;
} }
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, static void
struct sk_buff *skb) ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{ {
struct ieee80211_tdls_data *tf = (void *)skb->data; struct ieee80211_tdls_data *tf = (void *)skb->data;
struct wiphy *wiphy = sdata->local->hw.wiphy; struct wiphy *wiphy = sdata->local->hw.wiphy;
ASSERT_RTNL();
/* make sure the driver supports it */ /* make sure the driver supports it */
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
return; return;
...@@ -1847,3 +1851,29 @@ void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata) ...@@ -1847,3 +1851,29 @@ void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
} }
rcu_read_unlock(); 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();
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册