From 519ee6918b91abdc4bc9720deae17599a109eb40 Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Tue, 27 Oct 2015 08:35:11 +0100 Subject: 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(+) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8274c86..c5ed42f 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) { -- cgit v1.1 From 4baf6bea37247e59f1971e8009d13aeda95edba2 Mon Sep 17 00:00:00 2001 From: Ola Olsson Date: Thu, 29 Oct 2015 07:04:58 +0100 Subject: 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(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d693c9d..40ef5d6 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) { -- cgit v1.1 From 254d3dfe445f94a764e399ca12e04365ac9413ed Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 25 Oct 2015 10:59:41 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 5cf8f4e..56c6d6c 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 -- cgit v1.1 From a64cba3c5330704a034bd3179270b8d04daf6987 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 25 Oct 2015 10:59:38 +0200 Subject: 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(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ded4b97..73f1a2a 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, -- cgit v1.1 From 43d6df007c0cc111ab8ead8bd95aafab9bb4531d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Oct 2015 10:59:35 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 273c96d..858f6b1 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 c5ed42f..7f7ddc5 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 @@ -1754,6 +1755,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 * the device may at times not work immediately. Propagate -- cgit v1.1 From c189a685b83955a39884dc2bdae531bc0adf3b98 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 25 Oct 2015 10:59:40 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7f7ddc5..b0e3a42 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); -- cgit v1.1 From 968a76cef3d1bb9a3b4d135cd788056e742859f3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Oct 2015 10:59:36 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index a1d5431..9f97343 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 3098709..f82cfab 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 b0e3a42..551164d 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 -- cgit v1.1 From 0d440ea294a00b60ced66c0bc5cb5caa42fd4fbd Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 25 Oct 2015 10:59:33 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 713cdbf..c2bd1b6 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 62f2a97..68680ad 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 ad88ad4..00a43a7 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 b64fd2b..4aeca4b 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 551164d..d38daf0 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(); @@ -2043,32 +2066,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) 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. */ -- cgit v1.1 From 57f255f58165974c131f048b4302728052d92d29 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 25 Oct 2015 10:59:34 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 2001555..3b5874e 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 68680ad..5c76ba7 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 626e8de..fa28500 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 ecc5e2a..c9eeb3f 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 d38daf0..8802aa4 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; -- cgit v1.1 From 520c75dcae6e588670962243bac6324e7839b648 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 24 Oct 2015 21:25:51 +0200 Subject: 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(+) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index c1f8892..bd3d55e 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; -- cgit v1.1 From cec6628350802b0a652486c41b57d4e1dd37a65c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Oct 2015 17:46:04 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 3b5874e..337bb5d 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 5c76ba7..d832bd5 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 f848c75..d0dc1bf 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 73f1a2a..67f0387 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 8802aa4..7405802 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 */ -- cgit v1.1 From 730a755017139ddedac08d82f73c3532a020d372 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 22 Oct 2015 17:46:05 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 67f0387..b9534c9 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", -- cgit v1.1 From 2ed77ea69205139c3f6016b250d34e09bf48574d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 22 Oct 2015 17:46:06 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b9534c9..b140cc6 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. -- cgit v1.1 From ef95d8ba384781ce574c10f87b97d6bab2659735 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 25 Oct 2015 10:59:42 +0200 Subject: 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(-) (limited to 'net') diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index b07e2f7..a4e2f4e 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 = -- cgit v1.1 From dcae9e0203dfd887a7413cd38d1f87aaac1127f4 Mon Sep 17 00:00:00 2001 From: Chaitanya T K Date: Fri, 30 Oct 2015 23:16:15 +0530 Subject: 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 --- net/mac80211/driver-ops.c | 2 ++ net/mac80211/driver-ops.h | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'net') diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 9f97343..ca1fe55 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 f82cfab..154ce4b 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; -- cgit v1.1 From e4208427247ecc7306c8f71ab3c5c08e08cf9fda Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Fri, 23 Oct 2015 11:20:06 +0300 Subject: 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(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 40ef5d6..c71e274 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; -- cgit v1.1