From 519ee6918b91abdc4bc9720deae17599a109eb40 Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Tue, 27 Oct 2015 08:35:11 +0100 Subject: [PATCH 01/17] mac80211: fix divide by zero when NOA update In case of one shot NOA the interval can be 0, catch that instead of potentially (depending on the driver) crashing like this: divide error: 0000 [#1] SMP [...] Call Trace: [] ieee80211_extend_absent_time+0x6c/0xb0 [mac80211] [] ieee80211_update_p2p_noa+0xb7/0xe0 [mac80211] [] ath9k_p2p_ps_timer+0x170/0x190 [ath9k] [] ath_gen_timer_isr+0xc8/0xf0 [ath9k_hw] [] ath9k_tasklet+0x296/0x2f0 [ath9k] [] tasklet_action+0xe5/0xf0 [...] Cc: stable@vger.kernel.org [3.16+, due to d463af4a1c34 using it] Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- net/mac80211/util.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8274c86296f9..c5ed42f2f667 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2958,6 +2958,13 @@ ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) if (end > 0) return false; + /* One shot NOA */ + if (data->count[i] == 1) + return false; + + if (data->desc[i].interval == 0) + return false; + /* End time is in the past, check for repetitions */ skip = DIV_ROUND_UP(-end, data->desc[i].interval); if (data->count[i] < 255) { From 4baf6bea37247e59f1971e8009d13aeda95edba2 Mon Sep 17 00:00:00 2001 From: Ola Olsson Date: Thu, 29 Oct 2015 07:04:58 +0100 Subject: [PATCH 02/17] nl80211: Fix potential memory leak from parse_acl_data If parse_acl_data succeeds but the subsequent parsing of smps attributes fails, there will be a memory leak due to early returns. Fix that by moving the ACL parsing later. Cc: stable@vger.kernel.org Fixes: 18998c381b19b ("cfg80211: allow requesting SMPS mode on ap start") Signed-off-by: Ola Olsson Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d693c9d031fc..40ef5d604eb4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3432,12 +3432,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->iftype)) return -EINVAL; - if (info->attrs[NL80211_ATTR_ACL_POLICY]) { - params.acl = parse_acl_data(&rdev->wiphy, info); - if (IS_ERR(params.acl)) - return PTR_ERR(params.acl); - } - if (info->attrs[NL80211_ATTR_SMPS_MODE]) { params.smps_mode = nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]); @@ -3461,6 +3455,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params.smps_mode = NL80211_SMPS_OFF; } + if (info->attrs[NL80211_ATTR_ACL_POLICY]) { + params.acl = parse_acl_data(&rdev->wiphy, info); + if (IS_ERR(params.acl)) + return PTR_ERR(params.acl); + } + wdev_lock(wdev); err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { From 254d3dfe445f94a764e399ca12e04365ac9413ed Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 25 Oct 2015 10:59:41 +0200 Subject: [PATCH 03/17] mac80211: allow null chandef in tracing In TDLS channel-switch operations the chandef can sometimes be NULL. Avoid an oops in the trace code for these cases and just print a chandef full of zeros. Cc: stable@vger.kernel.org Fixes: a7a6bdd0670fe ("mac80211: introduce TDLS channel switch ops") Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/trace.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 5cf8f4ea077f..56c6d6cfa5a1 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -33,11 +33,11 @@ __field(u32, chan_width) \ __field(u32, center_freq1) \ __field(u32, center_freq2) -#define CHANDEF_ASSIGN(c) \ - __entry->control_freq = (c)->chan ? (c)->chan->center_freq : 0; \ - __entry->chan_width = (c)->width; \ - __entry->center_freq1 = (c)->center_freq1; \ - __entry->center_freq2 = (c)->center_freq2; +#define CHANDEF_ASSIGN(c) \ + __entry->control_freq = (c) ? ((c)->chan ? (c)->chan->center_freq : 0) : 0; \ + __entry->chan_width = (c) ? (c)->width : 0; \ + __entry->center_freq1 = (c) ? (c)->center_freq1 : 0; \ + __entry->center_freq2 = (c) ? (c)->center_freq2 : 0; #define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz" #define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \ __entry->center_freq1, __entry->center_freq2 From a64cba3c5330704a034bd3179270b8d04daf6987 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 25 Oct 2015 10:59:38 +0200 Subject: [PATCH 04/17] mac80211: Fix local deauth while associating Local request to deauthenticate wasn't handled while associating, thus the association could continue even when the user space required to disconnect. Cc: stable@vger.kernel.org Signed-off-by: Andrei Otcheretianski Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ded4b976bb48..73f1a2a0df5a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4936,6 +4936,25 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, return 0; } + if (ifmgd->assoc_data && + ether_addr_equal(ifmgd->assoc_data->bss->bssid, req->bssid)) { + sdata_info(sdata, + "aborting association with %pM by local choice (Reason: %u=%s)\n", + req->bssid, req->reason_code, + ieee80211_get_reason_code_string(req->reason_code)); + + drv_mgd_prepare_tx(sdata->local, sdata); + ieee80211_send_deauth_disassoc(sdata, req->bssid, + IEEE80211_STYPE_DEAUTH, + req->reason_code, tx, + frame_buf); + ieee80211_destroy_assoc_data(sdata, false); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + req->reason_code); + return 0; + } + if (ifmgd->associated && ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { sdata_info(sdata, From 43d6df007c0cc111ab8ead8bd95aafab9bb4531d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Oct 2015 10:59:35 +0200 Subject: [PATCH 05/17] mac80211: use freezable workqueue for restart work Requesting hw restart during suspend might result in the restart work being executed after mac80211 and the hw are suspended. Solve the race by simply scheduling the restart work on a freezable workqueue. Note that there can be some cases of reconfiguration on resume (besides the hardware restart): * wowlan is not configured - All the interfaces removed were removed on suspend, and drv_stop() was called. At this point the driver shouldn't expect for hw_restart anyway, so we can simply cancel it (on resume). * wowlan is configured, drv_resume() == 1 There is no definitive expected behavior in this case, as each driver might have different expectations (e.g. setting some flags on suspend/restart vs. not handling spurious recovery). For now, simply let the hw_restart work run again after resume, and hope the driver will handle it well (or at least initiate another hw restart). Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/main.c | 2 +- net/mac80211/util.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 273c96de4910..858f6b1cb149 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -281,7 +281,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) local->in_reconfig = true; barrier(); - schedule_work(&local->restart_work); + queue_work(system_freezable_wq, &local->restart_work); } EXPORT_SYMBOL(ieee80211_restart_hw); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index c5ed42f2f667..7f7ddc527baa 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4,6 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright (C) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1753,6 +1754,16 @@ int ieee80211_reconfig(struct ieee80211_local *local) } #endif + /* + * In case of hw_restart during suspend (without wowlan), + * cancel restart work, as we are reconfiguring the device + * anyway. + * Note that restart_work is scheduled on a frozen workqueue, + * so we can't deadlock in this case. + */ + if (suspended && local->in_reconfig && !reconfig_due_to_wowlan) + cancel_work_sync(&local->restart_work); + /* * Upon resume hardware can sometimes be goofy due to * various platform / driver / bus issues, so restarting From c189a685b83955a39884dc2bdae531bc0adf3b98 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 25 Oct 2015 10:59:40 +0200 Subject: [PATCH 06/17] mac80211: Remove WARN_ON_ONCE in ieee80211_recalc_smps The recalc_smps work can run after the station disassociates. At this stage we already released the channel, but the work will be cancelled only when the interface stops. In this scenario we can hit the warning in ieee80211_recalc_smps, so just remove it. Signed-off-by: Andrei Otcheretianski Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/util.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7f7ddc527baa..b0e3a4248bd5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2151,7 +2151,13 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, lockdep_is_held(&local->chanctx_mtx)); - if (WARN_ON_ONCE(!chanctx_conf)) + /* + * This function can be called from a work, thus it may be possible + * that the chanctx_conf is removed (due to a disconnection, for + * example). + * So nothing should be done in such case. + */ + if (!chanctx_conf) goto unlock; chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); From 968a76cef3d1bb9a3b4d135cd788056e742859f3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Oct 2015 10:59:36 +0200 Subject: [PATCH 07/17] mac80211: call drv_stop only if driver is started If drv_start() fails during hw_restart, all the running interfaces are being closed/stopped, which results in drv_stop() being called, although the driver was never started successfully. This might cause drivers to perform operations on uninitialized memory (as they assume it was initialized on drv_start) Consider the local->started flag, and call the driver's stop() op only if drv_start() succeeded before. Move drv_start() and drv_stop() to driver-ops.c, as they are no longer simple wrappers. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.c | 44 +++++++++++++++++++++++++++++++++++++++ net/mac80211/driver-ops.h | 32 ++-------------------------- net/mac80211/util.c | 3 ++- 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index a1d54318f16c..9f97343f13fd 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -1,4 +1,6 @@ /* + * Copyright 2015 Intel Deutschland GmbH + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -8,6 +10,48 @@ #include "trace.h" #include "driver-ops.h" +int drv_start(struct ieee80211_local *local) +{ + int ret; + + might_sleep(); + + if (WARN_ON(local->started)) + return -EALREADY; + + trace_drv_start(local); + local->started = true; + /* allow rx frames */ + smp_mb(); + ret = local->ops->start(&local->hw); + trace_drv_return_int(local, ret); + + if (ret) + local->started = false; + + return ret; +} + +void drv_stop(struct ieee80211_local *local) +{ + might_sleep(); + + if (WARN_ON(!local->started)) + return; + + trace_drv_stop(local); + local->ops->stop(&local->hw); + trace_drv_return_void(local); + + /* sync away all work on the tasklet before clearing started */ + tasklet_disable(&local->tasklet); + tasklet_enable(&local->tasklet); + + barrier(); + + local->started = false; +} + int drv_add_interface(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 30987099eb8f..f82cfab615f2 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -66,36 +66,8 @@ static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata, return rv; } -static inline int drv_start(struct ieee80211_local *local) -{ - int ret; - - might_sleep(); - - trace_drv_start(local); - local->started = true; - smp_mb(); - ret = local->ops->start(&local->hw); - trace_drv_return_int(local, ret); - return ret; -} - -static inline void drv_stop(struct ieee80211_local *local) -{ - might_sleep(); - - trace_drv_stop(local); - local->ops->stop(&local->hw); - trace_drv_return_void(local); - - /* sync away all work on the tasklet before clearing started */ - tasklet_disable(&local->tasklet); - tasklet_enable(&local->tasklet); - - barrier(); - - local->started = false; -} +int drv_start(struct ieee80211_local *local); +void drv_stop(struct ieee80211_local *local); #ifdef CONFIG_PM static inline int drv_suspend(struct ieee80211_local *local, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b0e3a4248bd5..551164dabbaf 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1665,7 +1665,6 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) local->resuming = false; local->suspended = false; - local->started = false; local->in_reconfig = false; /* scheduled scan clearly can't be running any more, but tell @@ -1764,6 +1763,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (suspended && local->in_reconfig && !reconfig_due_to_wowlan) cancel_work_sync(&local->restart_work); + local->started = false; + /* * Upon resume hardware can sometimes be goofy due to * various platform / driver / bus issues, so restarting From 0d440ea294a00b60ced66c0bc5cb5caa42fd4fbd Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Oct 2015 10:59:33 +0200 Subject: [PATCH 08/17] mac80211: don't reconfigure sched scan in case of wowlan Scheduled scan has to be reconfigured only if wowlan wasn't configured, since otherwise it should continue to run (with the 'any' trigger) or be aborted. The current code will end up asking the driver to start a new scheduled scan without stopping the previous one, and leaking some memory (from the previous request.) Fix this by doing the abort/restart under the proper conditions. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 6 ++--- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/pm.c | 11 +++++++++ net/mac80211/scan.c | 12 ++++++---- net/mac80211/util.c | 49 ++++++++++++++++++-------------------- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 713cdbf6fb3c..c2bd1b6a6922 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2010,12 +2010,12 @@ ieee80211_sched_scan_start(struct wiphy *wiphy, static int ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wiphy_priv(wiphy); - if (!sdata->local->ops->sched_scan_stop) + if (!local->ops->sched_scan_stop) return -EOPNOTSUPP; - return ieee80211_request_sched_scan_stop(sdata); + return ieee80211_request_sched_scan_stop(local); } static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 62f2a97cd2a6..68680ad04cef 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1573,7 +1573,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); -int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +int ieee80211_request_sched_scan_stop(struct ieee80211_local *local); void ieee80211_sched_scan_end(struct ieee80211_local *local); void ieee80211_sched_scan_stopped_work(struct work_struct *work); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index ad88ad4e8eb1..00a43a70e1fc 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,6 +6,13 @@ #include "driver-ops.h" #include "led.h" +static void ieee80211_sched_scan_cancel(struct ieee80211_local *local) +{ + if (ieee80211_request_sched_scan_stop(local)) + return; + cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy); +} + int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ieee80211_local *local = hw_to_local(hw); @@ -34,6 +41,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) mutex_unlock(&local->sta_mtx); } + /* keep sched_scan only in case of 'any' trigger */ + if (!(wowlan && wowlan->any)) + ieee80211_sched_scan_cancel(local); + ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_SUSPEND, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index b64fd2b2d95a..4aeca4b0c3cb 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1140,10 +1140,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, return ret; } -int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) +int ieee80211_request_sched_scan_stop(struct ieee80211_local *local) { - struct ieee80211_local *local = sdata->local; - int ret = 0; + struct ieee80211_sub_if_data *sched_scan_sdata; + int ret = -ENOENT; mutex_lock(&local->mtx); @@ -1155,8 +1155,10 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) /* We don't want to restart sched scan anymore. */ RCU_INIT_POINTER(local->sched_scan_req, NULL); - if (rcu_access_pointer(local->sched_scan_sdata)) { - ret = drv_sched_scan_stop(local, sdata); + sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, + lockdep_is_held(&local->mtx)); + if (sched_scan_sdata) { + ret = drv_sched_scan_stop(local, sched_scan_sdata); if (!ret) RCU_INIT_POINTER(local->sched_scan_sdata, NULL); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 551164dabbaf..d38daf0181e8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2008,6 +2008,29 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (ieee80211_sdata_running(sdata)) ieee80211_enable_keys(sdata); + /* Reconfigure sched scan if it was interrupted by FW restart */ + mutex_lock(&local->mtx); + sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, + lockdep_is_held(&local->mtx)); + sched_scan_req = rcu_dereference_protected(local->sched_scan_req, + lockdep_is_held(&local->mtx)); + if (sched_scan_sdata && sched_scan_req) + /* + * Sched scan stopped, but we don't want to report it. Instead, + * we're trying to reschedule. However, if more than one scan + * plan was set, we cannot reschedule since we don't know which + * scan plan was currently running (and some scan plans may have + * already finished). + */ + if (sched_scan_req->n_scan_plans > 1 || + __ieee80211_request_sched_scan_start(sched_scan_sdata, + sched_scan_req)) + sched_scan_stopped = true; + mutex_unlock(&local->mtx); + + if (sched_scan_stopped) + cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy); + wake_up: local->in_reconfig = false; barrier(); @@ -2042,32 +2065,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) IEEE80211_QUEUE_STOP_REASON_SUSPEND, false); - /* - * Reconfigure sched scan if it was interrupted by FW restart or - * suspend. - */ - mutex_lock(&local->mtx); - sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, - lockdep_is_held(&local->mtx)); - sched_scan_req = rcu_dereference_protected(local->sched_scan_req, - lockdep_is_held(&local->mtx)); - if (sched_scan_sdata && sched_scan_req) - /* - * Sched scan stopped, but we don't want to report it. Instead, - * we're trying to reschedule. However, if more than one scan - * plan was set, we cannot reschedule since we don't know which - * scan plan was currently running (and some scan plans may have - * already finished). - */ - if (sched_scan_req->n_scan_plans > 1 || - __ieee80211_request_sched_scan_start(sched_scan_sdata, - sched_scan_req)) - sched_scan_stopped = true; - mutex_unlock(&local->mtx); - - if (sched_scan_stopped) - cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy); - /* * If this is for hw restart things are still running. * We may want to change that later, however. From 57f255f58165974c131f048b4302728052d92d29 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 25 Oct 2015 10:59:34 +0200 Subject: [PATCH 09/17] mac80211: TDLS: add proper HT-oper IE When 11n peers performs a TDLS connection on a legacy BSS, the HT operation IE must be specified according to IEEE802.11-2012 section 9.23.3.2. Otherwise HT-protection is compromised and the medium becomes noisy for both the TDLS and the BSS links. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/mesh.c | 3 ++- net/mac80211/tdls.c | 13 ++++++++++--- net/mac80211/util.c | 5 ++++- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 2001555d49cb..3b5874e0174f 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -188,7 +188,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, * keep them at 0 */ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, - chandef, 0); + chandef, 0, false); /* add VHT capability and information IEs */ if (chandef->width != NL80211_CHAN_WIDTH_20 && diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 68680ad04cef..5c76ba7aaf57 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1962,7 +1962,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, const struct cfg80211_chan_def *chandef, - u16 prot_mode); + u16 prot_mode, bool rifs_mode); u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap); u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 626e8de70842..fa28500f28fd 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -466,7 +466,8 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef, - sdata->vif.bss_conf.ht_operation_mode); + sdata->vif.bss_conf.ht_operation_mode, + false); return 0; } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index ecc5e2a8f80b..c9eeb3f12808 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -591,12 +591,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, offset = noffset; } - /* if HT support is only added in TDLS, we need an HT-operation IE */ + /* + * if HT support is only added in TDLS, we need an HT-operation IE. + * add the IE as required by IEEE802.11-2012 9.23.3.2. + */ if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) { + u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | + IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); - /* send an empty HT operation IE */ ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap, - &sdata->vif.bss_conf.chandef, 0); + &sdata->vif.bss_conf.chandef, prot, + true); } ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d38daf0181e8..8802aa4da2a4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2292,7 +2292,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, const struct cfg80211_chan_def *chandef, - u16 prot_mode) + u16 prot_mode, bool rifs_mode) { struct ieee80211_ht_operation *ht_oper; /* Build HT Information */ @@ -2320,6 +2320,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, chandef->width != NL80211_CHAN_WIDTH_20) ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + if (rifs_mode) + ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE; + ht_oper->operation_mode = cpu_to_le16(prot_mode); ht_oper->stbc_param = 0x0000; From 520c75dcae6e588670962243bac6324e7839b648 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 24 Oct 2015 21:25:51 +0200 Subject: [PATCH 10/17] mac80211: fix crash on mesh local link ID generation with VIFs llid_in_use needs to be limited to stations of the same VIF, otherwise it will cause a NULL deref as the sta_info of non-mesh-VIFs don't have sta->mesh set. Steps to reproduce: modprobe mac80211_hwsim channels=2 iw phy phy0 interface add ibss0 type ibss iw phy phy0 interface add mesh0 type mp iw phy phy1 interface add ibss1 type ibss iw phy phy1 interface add mesh1 type mp ip link set ibss0 up ip link set mesh0 up ip link set ibss1 up ip link set mesh1 up iw dev ibss0 ibss join foo 2412 iw dev ibss1 ibss join foo 2412 # Ensure that ibss0 and ibss1 are actually associated; I often need to # leave and join the cell on ibss1 a second time. iw dev mesh0 mesh join bar iw dev mesh1 mesh join bar # crash Signed-off-by: Matthias Schiffer Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index c1f889270484..bd3d55eb21d4 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -686,6 +686,9 @@ static bool llid_in_use(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sdata != sta->sdata) + continue; + if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) { in_use = true; break; From e86abc689c5cb963f713c1bab9c37775421a6a96 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Oct 2015 17:35:14 +0200 Subject: [PATCH 11/17] cfg80211/mac80211: clarify RSSI CQM reporting requirements The previous patch changed mac80211 to always report an event after a CQM RSSI reconfiguration. Document that as expected behaviour in both the cfg80211 and mac80211 API. Currently, iwlmvm already implements that behaviour; the other drivers implementing CQM RSSI events may have to be changed. This behaviour lets userspace know what the current state is without relying on querying the data which is racy. Reviewed-by: Sharon, Sara Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/net/mac80211.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 48155be5db7f..2c7bdb81d30c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2399,6 +2399,10 @@ struct cfg80211_qos_map { * @set_power_mgmt: Configure WLAN power management. A timeout value of -1 * allows the driver to adjust the dynamic ps timeout value. * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. + * After configuration, the driver should (soon) send an event indicating + * the current level is above/below the configured threshold; this may + * need some care when the configuration is changed (without first being + * disabled.) * @set_cqm_txe_config: Configure connection quality monitor TX error * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4b9dd070aeb9..dac575c55c62 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -482,7 +482,9 @@ struct ieee80211_event { * Note that with TDLS this can be the case (channel is HT, protection must * be used from this field) even when the BSS association isn't using HT. * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value - * implies disabled + * implies disabled. As with the cfg80211 callback, a change here should + * cause an event to be sent indicating where the current value is in + * relation to the newly configured threshold. * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The * may filter ARP queries targeted for other addresses than listed here. From cec6628350802b0a652486c41b57d4e1dd37a65c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Oct 2015 17:46:04 +0200 Subject: [PATCH 12/17] mac80211: make enable_qos parameter to ieee80211_set_wmm_default() The function currently determines this value, for use in bss_info.qos, based on the interface type itself. Make it a parameter instead and set it with the same logic for now. Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/iface.c | 8 +++++--- net/mac80211/mlme.c | 4 ++-- net/mac80211/util.c | 11 ++--------- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 3b5874e0174f..337bb5d78003 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -356,7 +356,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; - ieee80211_set_wmm_default(sdata, true); + ieee80211_set_wmm_default(sdata, true, false); sdata->vif.bss_conf.ibss_joined = true; sdata->vif.bss_conf.ibss_creator = creator; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5c76ba7aaf57..d832bd59236b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1769,7 +1769,7 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len, int rate, int erp, int short_preamble, int shift); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, - bool bss_notify); + bool bss_notify, bool enable_qos); void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct sk_buff *skb); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f848c75518a2..d0dc1bfaeec2 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -661,11 +661,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) } /* - * set default queue parameters so drivers don't + * Set default queue parameters so drivers don't * need to initialise the hardware if the hardware - * doesn't start up with sane defaults + * doesn't start up with sane defaults. + * Enable QoS for anything but station interfaces. */ - ieee80211_set_wmm_default(sdata, true); + ieee80211_set_wmm_default(sdata, true, + sdata->vif.type != NL80211_IFTYPE_STATION); } set_bit(SDATA_STATE_RUNNING, &sdata->state); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 73f1a2a0df5a..67f0387bea27 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2077,7 +2077,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_bss_info_change_notify(sdata, changed); /* disassociated - set to defaults now */ - ieee80211_set_wmm_default(sdata, false); + ieee80211_set_wmm_default(sdata, false, false); del_timer_sync(&sdata->u.mgd.conn_mon_timer); del_timer_sync(&sdata->u.mgd.bcn_mon_timer); @@ -3048,7 +3048,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len); else - ieee80211_set_wmm_default(sdata, false); + ieee80211_set_wmm_default(sdata, false, false); changed |= BSS_CHANGED_QOS; /* set AID and assoc capability, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8802aa4da2a4..74058020b7d6 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1105,13 +1105,13 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, } void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, - bool bss_notify) + bool bss_notify, bool enable_qos) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_queue_params qparam; struct ieee80211_chanctx_conf *chanctx_conf; int ac; - bool use_11b, enable_qos; + bool use_11b; bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */ int aCWmin, aCWmax; @@ -1130,13 +1130,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); rcu_read_unlock(); - /* - * By default disable QoS in STA mode for old access points, which do - * not support 802.11e. New APs will provide proper queue parameters, - * that we will configure later. - */ - enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); - is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB); /* Set defaults according to 802.11-2007 Table 7-37 */ From 730a755017139ddedac08d82f73c3532a020d372 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 22 Oct 2015 17:46:05 +0200 Subject: [PATCH 13/17] mac80211: fixup AIFSN instead of disabling WMM Disabling WMM has a huge impact these days. It implies that HT and VHT will be disabled which means that the throughput will be drammatically reduced. Since the AIFSN is a transmission parameter, we can play a bit and fix it up to make it compliant with the 802.11 specification which requires it to be at least 2. Increasing it from 1 to 2 will slightly reduce the likelyhood to get a transmission opportunity compared to other clients that would accept to set AIFSN=1, but at least it will allow HT and VHT which is a huge gain. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 67f0387bea27..b9534c9902ac 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1816,6 +1816,13 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, } params.aifs = pos[0] & 0x0f; + + if (params.aifs < 2) { + sdata_info(sdata, + "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n", + params.aifs, aci); + params.aifs = 2; + } params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); params.cw_min = ecw2cw(pos[1] & 0x0f); params.txop = get_unaligned_le16(pos + 2); @@ -4559,17 +4566,10 @@ static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata, left = len - 8; for (; left >= 4; left -= 4, pos += 4) { - u8 aifsn = pos[0] & 0x0f; u8 ecwmin = pos[1] & 0x0f; u8 ecwmax = (pos[1] & 0xf0) >> 4; int aci = (pos[0] >> 5) & 0x03; - if (aifsn < 2) { - sdata_info(sdata, - "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n", - aifsn, aci); - return false; - } if (ecwmin > ecwmax) { sdata_info(sdata, "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n", From 2ed77ea69205139c3f6016b250d34e09bf48574d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Oct 2015 17:46:06 +0200 Subject: [PATCH 14/17] mac80211: treat bad WMM parameters more gracefully As WMM is required for HT/VHT operation, treat bad WMM parameters more gracefully by falling back to default parameters instead of not using WMM assocation. This makes it possible to still use HT or VHT, although potentially with reduced quality of service due to unintended WMM parameters. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 142 +++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 94 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b9534c9902ac..b140cc6651f4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1744,10 +1744,10 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, const u8 *wmm_param, size_t wmm_param_len) { - struct ieee80211_tx_queue_params params; + struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t left; - int count; + int count, ac; const u8 *pos; u8 uapsd_queues = 0; @@ -1781,25 +1781,24 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, int aci = (pos[0] >> 5) & 0x03; int acm = (pos[0] >> 4) & 0x01; bool uapsd = false; - int queue; switch (aci) { case 1: /* AC_BK */ - queue = 3; + ac = IEEE80211_AC_BK; if (acm) sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) uapsd = true; break; case 2: /* AC_VI */ - queue = 1; + ac = IEEE80211_AC_VI; if (acm) sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) uapsd = true; break; case 3: /* AC_VO */ - queue = 0; + ac = IEEE80211_AC_VO; if (acm) sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) @@ -1807,7 +1806,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, break; case 0: /* AC_BE */ default: - queue = 2; + ac = IEEE80211_AC_BE; if (acm) sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) @@ -1815,32 +1814,41 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, break; } - params.aifs = pos[0] & 0x0f; + params[ac].aifs = pos[0] & 0x0f; - if (params.aifs < 2) { + if (params[ac].aifs < 2) { sdata_info(sdata, "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n", - params.aifs, aci); - params.aifs = 2; + params[ac].aifs, aci); + params[ac].aifs = 2; } - params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); - params.cw_min = ecw2cw(pos[1] & 0x0f); - params.txop = get_unaligned_le16(pos + 2); - params.acm = acm; - params.uapsd = uapsd; + params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4); + params[ac].cw_min = ecw2cw(pos[1] & 0x0f); + params[ac].txop = get_unaligned_le16(pos + 2); + params[ac].acm = acm; + params[ac].uapsd = uapsd; + if (params[ac].cw_min > params[ac].cw_max) { + sdata_info(sdata, + "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n", + params[ac].cw_min, params[ac].cw_max, aci); + return false; + } + } + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { mlme_dbg(sdata, - "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", - queue, aci, acm, - params.aifs, params.cw_min, params.cw_max, - params.txop, params.uapsd, - ifmgd->tx_tspec[queue].downgraded); - sdata->tx_conf[queue] = params; - if (!ifmgd->tx_tspec[queue].downgraded && - drv_conf_tx(local, sdata, queue, ¶ms)) + "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", + ac, params[ac].acm, + params[ac].aifs, params[ac].cw_min, params[ac].cw_max, + params[ac].txop, params[ac].uapsd, + ifmgd->tx_tspec[ac].downgraded); + sdata->tx_conf[ac] = params[ac]; + if (!ifmgd->tx_tspec[ac].downgraded && + drv_conf_tx(local, sdata, ac, ¶ms[ac])) sdata_err(sdata, - "failed to set TX queue parameters for queue %d\n", - queue); + "failed to set TX queue parameters for AC %d\n", + ac); } /* enable WMM or activate new settings */ @@ -3051,11 +3059,21 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, */ ifmgd->wmm_last_param_set = -1; - if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param) - ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, - elems.wmm_param_len); - else + if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { ieee80211_set_wmm_default(sdata, false, false); + } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, + elems.wmm_param_len)) { + /* still enable QoS since we might have HT/VHT */ + ieee80211_set_wmm_default(sdata, false, true); + /* set the disable-WMM flag in this case to disable + * tracking WMM parameter changes in the beacon if + * the parameters weren't actually valid. Doing so + * avoids changing parameters very strangely when + * the AP is going back and forth between valid and + * invalid parameters. + */ + ifmgd->flags |= IEEE80211_STA_DISABLE_WMM; + } changed |= BSS_CHANGED_QOS; /* set AID and assoc capability, @@ -4550,37 +4568,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return err; } -static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata, - const u8 *wmm_param, int len) -{ - const u8 *pos; - size_t left; - - if (len < 8) - return false; - - if (wmm_param[5] != 1 /* version */) - return false; - - pos = wmm_param + 8; - left = len - 8; - - for (; left >= 4; left -= 4, pos += 4) { - u8 ecwmin = pos[1] & 0x0f; - u8 ecwmax = (pos[1] & 0xf0) >> 4; - int aci = (pos[0] >> 5) & 0x03; - - if (ecwmin > ecwmax) { - sdata_info(sdata, - "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n", - ecwmin, ecwmax, aci); - return false; - } - } - - return true; -} - int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req) { @@ -4645,39 +4632,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->wmm = bss->wmm_used && (local->hw.queues >= IEEE80211_NUM_ACS); - if (assoc_data->wmm) { - /* try to check validity of WMM params IE */ - const struct cfg80211_bss_ies *ies; - const u8 *wp, *start, *end; - - rcu_read_lock(); - ies = rcu_dereference(req->bss->ies); - start = ies->data; - end = start + ies->len; - - while (true) { - wp = cfg80211_find_vendor_ie( - WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WMM, - start, end - start); - if (!wp) - break; - start = wp + wp[1] + 2; - /* if this IE is too short, try the next */ - if (wp[1] <= 4) - continue; - /* if this IE is WMM params, we found what we wanted */ - if (wp[6] == 1) - break; - } - - if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2, - wp[1] - 2)) { - assoc_data->wmm = false; - ifmgd->flags |= IEEE80211_STA_DISABLE_WMM; - } - rcu_read_unlock(); - } /* * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. From ef95d8ba384781ce574c10f87b97d6bab2659735 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 25 Oct 2015 10:59:42 +0200 Subject: [PATCH 15/17] mac80211: further improve "no supported rates" warning Allow distinguishing the non-station case from the case of a station without rates, by using -1 for the non-station case. This value cannot be reached with a station since that many legacy rates don't exist. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/rate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index b07e2f748f93..a4e2f4e67f94 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -307,7 +307,7 @@ static void __rate_control_send_low(struct ieee80211_hw *hw, } WARN_ONCE(i == sband->n_bitrates, "no supported rates (0x%x) in rate_mask 0x%x with flags 0x%x\n", - sta ? sta->supp_rates[sband->band] : 0, + sta ? sta->supp_rates[sband->band] : -1, rate_mask, rate_flags); info->control.rates[0].count = From dcae9e0203dfd887a7413cd38d1f87aaac1127f4 Mon Sep 17 00:00:00 2001 From: Chaitanya T K Date: Fri, 30 Oct 2015 23:16:15 +0530 Subject: [PATCH 16/17] mac80211: document sleep requirements for channel context ops Channel context driver operations can sleep, so add might_sleep() and document this. Signed-off-by: Chaitanya T K Signed-off-by: Johannes Berg --- include/net/mac80211.h | 6 ++++++ net/mac80211/driver-ops.c | 2 ++ net/mac80211/driver-ops.h | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dac575c55c62..82045fca388b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3172,18 +3172,24 @@ enum ieee80211_reconfig_type { * The callback is optional and can sleep. * * @add_chanctx: Notifies device driver about new channel context creation. + * This callback may sleep. * @remove_chanctx: Notifies device driver about channel context destruction. + * This callback may sleep. * @change_chanctx: Notifies device driver about channel context changes that * may happen when combining different virtual interfaces on the same * channel context with different settings + * This callback may sleep. * @assign_vif_chanctx: Notifies device driver about channel context being bound * to vif. Possible use is for hw queue remapping. + * This callback may sleep. * @unassign_vif_chanctx: Notifies device driver about channel context being * unbound from vif. + * This callback may sleep. * @switch_vif_chanctx: switch a number of vifs from one chanctx to * another, as specified in the list of * @ieee80211_vif_chanctx_switch passed to the driver, according * to the mode defined in &ieee80211_chanctx_switch_mode. + * This callback may sleep. * * @start_ap: Start operation on the AP interface, this is called after all the * information in bss_conf is set and beacon can be retrieved. A channel diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 9f97343f13fd..ca1fe5576103 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -236,6 +236,8 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local, int ret = 0; int i; + might_sleep(); + if (!local->ops->switch_vif_chanctx) return -EOPNOTSUPP; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index f82cfab615f2..154ce4b13406 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -843,6 +843,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, { int ret = -EOPNOTSUPP; + might_sleep(); + trace_drv_add_chanctx(local, ctx); if (local->ops->add_chanctx) ret = local->ops->add_chanctx(&local->hw, &ctx->conf); @@ -856,6 +858,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, static inline void drv_remove_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { + might_sleep(); + if (WARN_ON(!ctx->driver_present)) return; @@ -870,6 +874,8 @@ static inline void drv_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, u32 changed) { + might_sleep(); + trace_drv_change_chanctx(local, ctx, changed); if (local->ops->change_chanctx) { WARN_ON_ONCE(!ctx->driver_present); @@ -903,6 +909,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx *ctx) { + might_sleep(); + if (!check_sdata_in_driver(sdata)) return; @@ -925,6 +933,8 @@ static inline int drv_start_ap(struct ieee80211_local *local, { int ret = 0; + might_sleep(); + if (!check_sdata_in_driver(sdata)) return -EIO; From e4208427247ecc7306c8f71ab3c5c08e08cf9fda Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Fri, 23 Oct 2015 11:20:06 +0300 Subject: [PATCH 17/17] cfg80211: allow AID/listen interval changes for unassociated station Currently, cfg80211 rejects updates of AID and listen interval parameters for existing entries. This information is known only at association stage and as a result it's impossible to update entries that were added unassociated. Fix this by allowing updates of these properies for stations that the driver (or mac80211) assigned unassociated state. This then fixes mac80211's use of NL80211_FEATURE_FULL_AP_CLIENT_STATE. Signed-off-by: Ayala Beker Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 40ef5d604eb4..c71e274c810a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3968,10 +3968,13 @@ int cfg80211_check_station_change(struct wiphy *wiphy, struct station_parameters *params, enum cfg80211_station_type statype) { - if (params->listen_interval != -1) + if (params->listen_interval != -1 && + statype != CFG80211_STA_AP_CLIENT_UNASSOC) return -EINVAL; + if (params->aid && - !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && + statype != CFG80211_STA_AP_CLIENT_UNASSOC) return -EINVAL; /* When you run into this, adjust the code below for the new flag */ @@ -4245,13 +4248,22 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); - params.listen_interval = -1; - if (!rdev->ops->change_station) return -EOPNOTSUPP; - if (info->attrs[NL80211_ATTR_STA_AID]) - return -EINVAL; + /* + * AID and listen_interval properties can be set only for unassociated + * station. Include these parameters here and will check them in + * cfg80211_check_station_change(). + */ + if (info->attrs[NL80211_ATTR_PEER_AID]) + params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); + + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) + params.listen_interval = + nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); + else + params.listen_interval = -1; if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; @@ -4278,9 +4290,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); } - if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - return -EINVAL; - if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL;