Commit e487eaeb authored by Simon Wunderlich's avatar Simon Wunderlich Committed by Johannes Berg

cfg80211/mac80211/ath6kl: acquire wdev lock outside ch_switch_notify

The channel switch notification should be sent under the
wdev/sdata-lock, preferably in the same moment as the channel change
happens, to avoid races by other callers (e.g. start/stop_ap).
This also adds the previously missing sdata_lock protection in
csa_finalize_work.
Reported-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarSimon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 7ca133bc
...@@ -1109,7 +1109,9 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, ...@@ -1109,7 +1109,9 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,
(mode == WMI_11G_HT20) ? (mode == WMI_11G_HT20) ?
NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT);
mutex_lock(&vif->wdev.mtx);
cfg80211_ch_switch_notify(vif->ndev, &chandef); cfg80211_ch_switch_notify(vif->ndev, &chandef);
mutex_unlock(&vif->wdev.mtx);
} }
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
......
...@@ -4286,7 +4286,8 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, ...@@ -4286,7 +4286,8 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
* @dev: the device which switched channels * @dev: the device which switched channels
* @chandef: the new channel definition * @chandef: the new channel definition
* *
* Acquires wdev_lock, so must only be called from sleepable driver context! * Caller must acquire wdev_lock, therefore must only be called from sleepable
* driver context!
*/ */
void cfg80211_ch_switch_notify(struct net_device *dev, void cfg80211_ch_switch_notify(struct net_device *dev,
struct cfg80211_chan_def *chandef); struct cfg80211_chan_def *chandef);
......
...@@ -2988,13 +2988,18 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ...@@ -2988,13 +2988,18 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
int err, changed = 0; int err, changed = 0;
sdata_lock(sdata);
/* AP might have been stopped while waiting for the lock. */
if (!sdata->vif.csa_active)
goto unlock;
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
return; goto unlock;
sdata->radar_required = sdata->csa_radar_required; sdata->radar_required = sdata->csa_radar_required;
err = ieee80211_vif_change_channel(sdata, &changed); err = ieee80211_vif_change_channel(sdata, &changed);
if (WARN_ON(err < 0)) if (WARN_ON(err < 0))
return; goto unlock;
if (!local->use_chanctx) { if (!local->use_chanctx) {
local->_oper_chandef = sdata->csa_chandef; local->_oper_chandef = sdata->csa_chandef;
...@@ -3003,11 +3008,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ...@@ -3003,11 +3008,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
ieee80211_bss_info_change_notify(sdata, changed); ieee80211_bss_info_change_notify(sdata, changed);
sdata->vif.csa_active = false;
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
if (err < 0) if (err < 0)
return; goto unlock;
changed |= err; changed |= err;
kfree(sdata->u.ap.next_beacon); kfree(sdata->u.ap.next_beacon);
sdata->u.ap.next_beacon = NULL; sdata->u.ap.next_beacon = NULL;
...@@ -3021,20 +3028,22 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ...@@ -3021,20 +3028,22 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
err = ieee80211_mesh_finish_csa(sdata); err = ieee80211_mesh_finish_csa(sdata);
if (err < 0) if (err < 0)
return; goto unlock;
break; break;
#endif #endif
default: default:
WARN_ON(1); WARN_ON(1);
return; goto unlock;
} }
sdata->vif.csa_active = false;
ieee80211_wake_queues_by_reason(&sdata->local->hw, ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP, IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA); IEEE80211_QUEUE_STOP_REASON_CSA);
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
unlock:
sdata_unlock(sdata);
} }
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
......
...@@ -10821,21 +10821,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev, ...@@ -10821,21 +10821,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
struct wiphy *wiphy = wdev->wiphy; struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
trace_cfg80211_ch_switch_notify(dev, chandef); ASSERT_WDEV_LOCK(wdev);
wdev_lock(wdev); trace_cfg80211_ch_switch_notify(dev, chandef);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO && wdev->iftype != NL80211_IFTYPE_P2P_GO &&
wdev->iftype != NL80211_IFTYPE_ADHOC && wdev->iftype != NL80211_IFTYPE_ADHOC &&
wdev->iftype != NL80211_IFTYPE_MESH_POINT)) wdev->iftype != NL80211_IFTYPE_MESH_POINT))
goto out; return;
wdev->channel = chandef->chan; wdev->channel = chandef->chan;
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
out:
wdev_unlock(wdev);
return;
} }
EXPORT_SYMBOL(cfg80211_ch_switch_notify); EXPORT_SYMBOL(cfg80211_ch_switch_notify);
......
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