Commit ae2e9fba authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Johannes Berg

mac80211: allow TDLS setup code to take wdev lock

TDLS off-channel can be allowed in channels marked with GO_CONCURRENT,
provided the device is connected to an AP on the same UNII.
When relaxing the NO-IR requirements for TDLS, we might hit flows in
cfg80211_reg_can_beacon that acquire the wdev lock. Take some measures
to allow this during TDLS setup.
Acquire the RCU read lock later in the flow that invokes
cfg80211_reg_can_beacon.
Avoid taking local->mtx when preparing the setup packet to avoid
circular deadlocks with mac80211 code that is invoked with wdev-mtx
held.
Signed-off-by: default avatarArik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 23e37098
...@@ -287,17 +287,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ...@@ -287,17 +287,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
size_t offset = 0, noffset; size_t offset = 0, noffset;
u8 *pos; u8 *pos;
rcu_read_lock();
/* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock();
return;
}
}
ieee80211_add_srates_ie(sdata, skb, false, band); ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band); ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_supp_channels(sdata, skb); ieee80211_tdls_add_supp_channels(sdata, skb);
...@@ -350,6 +339,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ...@@ -350,6 +339,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset; offset = noffset;
} }
rcu_read_lock();
/* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock();
return;
}
}
/* /*
* with TDLS we can switch channels, and HT-caps are not necessarily * with TDLS we can switch channels, and HT-caps are not necessarily
* the same on all bands. The specification limits the setup to a * the same on all bands. The specification limits the setup to a
...@@ -983,7 +983,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, ...@@ -983,7 +983,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
ret = -EBUSY; ret = -EBUSY;
goto exit; goto out_unlock;
} }
/* /*
...@@ -998,27 +998,34 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, ...@@ -998,27 +998,34 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (!sta_info_get(sdata, peer)) { if (!sta_info_get(sdata, peer)) {
rcu_read_unlock(); rcu_read_unlock();
ret = -ENOLINK; ret = -ENOLINK;
goto exit; goto out_unlock;
} }
rcu_read_unlock(); rcu_read_unlock();
} }
ieee80211_flush_queues(local, sdata, false); ieee80211_flush_queues(local, sdata, false);
memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
mutex_unlock(&local->mtx);
/* we cannot take the mutex while preparing the setup packet */
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code, dialog_token, status_code,
peer_capability, initiator, peer_capability, initiator,
extra_ies, extra_ies_len, 0, extra_ies, extra_ies_len, 0,
NULL); NULL);
if (ret < 0) if (ret < 0) {
goto exit; mutex_lock(&local->mtx);
eth_zero_addr(sdata->u.mgd.tdls_peer);
mutex_unlock(&local->mtx);
return ret;
}
memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
ieee80211_queue_delayed_work(&sdata->local->hw, ieee80211_queue_delayed_work(&sdata->local->hw,
&sdata->u.mgd.tdls_peer_del_work, &sdata->u.mgd.tdls_peer_del_work,
TDLS_PEER_SETUP_TIMEOUT); TDLS_PEER_SETUP_TIMEOUT);
return 0;
exit: out_unlock:
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
return ret; return ret;
} }
......
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