From e4a2eb8995365858f0dad6985b119313f0a4624f Mon Sep 17 00:00:00 2001 From: Bjoern Johansson Date: Wed, 11 Apr 2018 11:41:56 -0700 Subject: [PATCH 01/30] mac80211_hwsim: indicate support for powersave. Without this, higher layers in the kernel will return an error code when trying to set the power state because the driver doesn't indicate power state support. This in turn causes VTS (Android Vendor Test Suite) failures because the WiFi HAL can't enable power saving mode. Signed-off-by: Bjoern Johansson Signed-off-by: Lingfeng Yang Signed-off-by: Roman Kiryanov [johannes: remove remaining code, it was useless even as a skeleton since it didn't even have the right function arguments] Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 96d26cfae90b..7bfbc5916e9a 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2650,6 +2650,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, AMPDU_AGGREGATION); ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, TDLS_WIDER_BW); if (rctbl) ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); From 8db0c433692eda3a0358d72643bd0268b736767c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 19 Apr 2018 11:17:38 +0200 Subject: [PATCH 02/30] regulatory: Rename confusing 'country IE' in log output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'country IE' messages in the log can be confusing and make people think that the country code has been set to Ireland. Fix this by changing the log messages to use 'country element' instead (as they are no longer called 'information element' in the spec anyway). Reported-by: Bernhard Gabler Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 16c7e4ef5820..ecfee5f06c76 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1652,7 +1652,7 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator) case NL80211_REGDOM_SET_BY_DRIVER: return "driver"; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - return "country IE"; + return "country element"; default: WARN_ON(1); return "bug"; @@ -2618,7 +2618,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy, * This doesn't happen yet, not sure we * ever want to support it for this case. */ - WARN_ONCE(1, "Unexpected intersection for country IEs"); + WARN_ONCE(1, "Unexpected intersection for country elements"); return REG_REQ_IGNORE; } From c096b92aa79e603995982c4d324a52ee02775b85 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Apr 2018 13:49:18 +0300 Subject: [PATCH 03/30] mac80211: rename rtap_vendor_space to rtap_space Since all the HE data won't fit into struct ieee80211_rx_status, we'll (have to) move that into the SKB proper. This means we'll need to skip over more things in the future, so rename this to remove the vendor-only notion from it. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 03102aff0953..0a38cc1cbebc 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -5,6 +5,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * 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 @@ -97,27 +98,27 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, */ static void remove_monitor_info(struct sk_buff *skb, unsigned int present_fcs_len, - unsigned int rtap_vendor_space) + unsigned int rtap_space) { if (present_fcs_len) __pskb_trim(skb, skb->len - present_fcs_len); - __pskb_pull(skb, rtap_vendor_space); + __pskb_pull(skb, rtap_space); } static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len, - unsigned int rtap_vendor_space) + unsigned int rtap_space) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr; - hdr = (void *)(skb->data + rtap_vendor_space); + hdr = (void *)(skb->data + rtap_space); if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC | RX_FLAG_ONLY_MONITOR)) return true; - if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space)) + if (unlikely(skb->len < 16 + present_fcs_len + rtap_space)) return true; if (ieee80211_is_ctl(hdr->frame_control) && @@ -199,7 +200,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - int rtap_vendor_space) + int rtap_space) { struct { struct ieee80211_hdr_3addr hdr; @@ -212,14 +213,14 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata, BUILD_BUG_ON(sizeof(action) != IEEE80211_MIN_ACTION_SIZE + 1); - if (skb->len < rtap_vendor_space + sizeof(action) + + if (skb->len < rtap_space + sizeof(action) + VHT_MUMIMO_GROUPS_DATA_LEN) return; if (!is_valid_ether_addr(sdata->u.mntr.mu_follow_addr)) return; - skb_copy_bits(skb, rtap_vendor_space, &action, sizeof(action)); + skb_copy_bits(skb, rtap_space, &action, sizeof(action)); if (!ieee80211_is_action(action.hdr.frame_control)) return; @@ -545,7 +546,7 @@ static struct sk_buff * ieee80211_make_monitor_skb(struct ieee80211_local *local, struct sk_buff **origskb, struct ieee80211_rate *rate, - int rtap_vendor_space, bool use_origskb) + int rtap_space, bool use_origskb) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(*origskb); int rt_hdrlen, needed_headroom; @@ -553,7 +554,7 @@ ieee80211_make_monitor_skb(struct ieee80211_local *local, /* room for the radiotap header based on driver features */ rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, *origskb); - needed_headroom = rt_hdrlen - rtap_vendor_space; + needed_headroom = rt_hdrlen - rtap_space; if (use_origskb) { /* only need to expand headroom if necessary */ @@ -607,7 +608,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, struct ieee80211_sub_if_data *sdata; struct sk_buff *monskb = NULL; int present_fcs_len = 0; - unsigned int rtap_vendor_space = 0; + unsigned int rtap_space = 0; struct ieee80211_sub_if_data *monitor_sdata = rcu_dereference(local->monitor_sdata); bool only_monitor = false; @@ -615,7 +616,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; - rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad; + rtap_space += sizeof(*rtap) + rtap->len + rtap->pad; } /* @@ -638,13 +639,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, } /* ensure hdr->frame_control and vendor radiotap data are in skb head */ - if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) { + if (!pskb_may_pull(origskb, 2 + rtap_space)) { dev_kfree_skb(origskb); return NULL; } - only_monitor = should_drop_frame(origskb, present_fcs_len, - rtap_vendor_space); + only_monitor = should_drop_frame(origskb, present_fcs_len, rtap_space); if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) { if (only_monitor) { @@ -652,12 +652,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return NULL; } - remove_monitor_info(origskb, present_fcs_len, - rtap_vendor_space); + remove_monitor_info(origskb, present_fcs_len, rtap_space); return origskb; } - ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space); + ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space); list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) { bool last_monitor = list_is_last(&sdata->u.mntr.list, @@ -665,8 +664,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (!monskb) monskb = ieee80211_make_monitor_skb(local, &origskb, - rate, - rtap_vendor_space, + rate, rtap_space, only_monitor && last_monitor); @@ -698,7 +696,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (!origskb) return NULL; - remove_monitor_info(origskb, present_fcs_len, rtap_vendor_space); + remove_monitor_info(origskb, present_fcs_len, rtap_space); return origskb; } From 35f4962c01757db6cc1dcdf5526ec6e5afcb6245 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Apr 2018 13:49:21 +0300 Subject: [PATCH 04/30] mac80211: clean up rate info bandwidth setting There's no need to do the same thing three times in the different switch cases, pull that out to a single place. Signed-off-by: Johannes Berg --- net/mac80211/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 11f9cfc016d9..2d82c88efd0b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2793,12 +2793,13 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, memset(&ri, 0, sizeof(ri)); + ri.bw = status->bw; + /* Fill cfg80211 rate info */ switch (status->encoding) { case RX_ENC_HT: ri.mcs = status->rate_idx; ri.flags |= RATE_INFO_FLAGS_MCS; - ri.bw = status->bw; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; break; @@ -2806,7 +2807,6 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, ri.flags |= RATE_INFO_FLAGS_VHT_MCS; ri.mcs = status->rate_idx; ri.nss = status->nss; - ri.bw = status->bw; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; break; @@ -2818,8 +2818,6 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, int shift = 0; int bitrate; - ri.bw = status->bw; - switch (status->bw) { case RATE_INFO_BW_10: shift = 1; From bfb27814e3d89a6abf362542bc4b26d84debc5b6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Apr 2018 13:49:22 +0300 Subject: [PATCH 05/30] mac80211: ethtool: memset the whole sinfo struct to 0 Rather than just setting the valid flags to 0 set the whole struct to 0 since other places might rely on it. Signed-off-by: Johannes Berg --- net/mac80211/ethtool.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 9cc986deda61..08408520c3f8 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -4,6 +4,7 @@ * Copied from cfg.c - originally * Copyright 2006-2010 Johannes Berg * Copyright 2014 Intel Corporation (Author: Johannes Berg) + * Copyright (C) 2018 Intel Corporation * * This file is GPLv2 as found in COPYING. */ @@ -106,7 +107,7 @@ static void ieee80211_get_stats(struct net_device *dev, if (!(sta && !WARN_ON(sta->sdata->dev != dev))) goto do_survey; - sinfo.filled = 0; + memset(&sinfo, 0, sizeof(sinfo)); sta_set_sinfo(sta, &sinfo); i = 0; @@ -133,7 +134,7 @@ static void ieee80211_get_stats(struct net_device *dev, if (sta->sdata->dev != dev) continue; - sinfo.filled = 0; + memset(&sinfo, 0, sizeof(sinfo)); sta_set_sinfo(sta, &sinfo); i = 0; ADD_STA_STATS(sta); From a403f3bf6390f085b7864814fb17567718c5a22c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Apr 2018 13:49:23 +0300 Subject: [PATCH 06/30] mac80211: remove pointless flags=0 assignment The data structure is initialized to all zeroes, and we already rely on that in other places, so remove the pointless assignment to 0. Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 655c3d8b0d80..2d5ffb0a16f6 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -3,6 +3,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * 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 @@ -1992,7 +1993,6 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, int band = STA_STATS_GET(LEGACY_BAND, rate); int rate_idx = STA_STATS_GET(LEGACY_IDX, rate); - rinfo->flags = 0; sband = local->hw.wiphy->bands[band]; brate = sband->bitrates[rate_idx].bitrate; if (rinfo->bw == RATE_INFO_BW_5) From 03737001e463d458c13510fcb64179b3368aaeee Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Fri, 20 Apr 2018 13:49:24 +0300 Subject: [PATCH 07/30] mac80211: add api to set CSA counter in mac80211 Sometimes the most updated CSA counter values are known only to the device. Add an API to pass this data to mac80211. Signed-off-by: Gregory Greenman Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 13 +++++++++++++ net/mac80211/tx.c | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d2279b2d61aa..52f36c43f35f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4449,6 +4449,19 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, */ u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif); +/** + * ieee80211_csa_set_counter - request mac80211 to set csa counter + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @counter: the new value for the counter + * + * The csa counter can be changed by the device, this API should be + * used by the device driver to update csa counter in mac80211. + * + * It should never be used together with ieee80211_csa_update_counter(), + * as it will cause a race condition around the counter value. + */ +void ieee80211_csa_set_counter(struct ieee80211_vif *vif, u8 counter); + /** * ieee80211_csa_finish - notify mac80211 about channel switch * @vif: &struct ieee80211_vif pointer from the add_interface callback. diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 535de3161a78..062e125a324c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4084,6 +4084,31 @@ unlock: } EXPORT_SYMBOL(ieee80211_csa_update_counter); +void ieee80211_csa_set_counter(struct ieee80211_vif *vif, u8 counter) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct beacon_data *beacon = NULL; + + rcu_read_lock(); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + beacon = rcu_dereference(sdata->u.ap.beacon); + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + beacon = rcu_dereference(sdata->u.ibss.presp); + else if (ieee80211_vif_is_mesh(&sdata->vif)) + beacon = rcu_dereference(sdata->u.mesh.beacon); + + if (!beacon) + goto unlock; + + if (counter < beacon->csa_current_counter) + beacon->csa_current_counter = counter; + +unlock: + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee80211_csa_set_counter); + bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); From 50f32718e125c3be5b0528bfa3868e88d677d8ce Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Fri, 20 Apr 2018 13:49:26 +0300 Subject: [PATCH 08/30] nl80211: Add wmm rule attribute to NL80211_CMD_GET_WIPHY dump command This will serve userspace entity to maintain its regulatory limitation. More specifcally APs can use this data to calculate the WMM IE when building: beacons, probe responses, assoc responses etc... Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 28 ++++++++++++++++++ net/wireless/nl80211.c | 57 +++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 15daf5e2638d..04c9b97aa5fc 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -11,6 +11,7 @@ * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -3141,6 +3142,29 @@ enum nl80211_band_attr { #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA +/** + * enum nl80211_wmm_rule - regulatory wmm rule + * + * @__NL80211_WMMR_INVALID: attribute number 0 is reserved + * @NL80211_WMMR_CW_MIN: Minimum contention window slot. + * @NL80211_WMMR_CW_MAX: Maximum contention window slot. + * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space. + * @NL80211_WMMR_TXOP: Maximum allowed tx operation time. + * @nl80211_WMMR_MAX: highest possible wmm rule. + * @__NL80211_WMMR_LAST: Internal use. + */ +enum nl80211_wmm_rule { + __NL80211_WMMR_INVALID, + NL80211_WMMR_CW_MIN, + NL80211_WMMR_CW_MAX, + NL80211_WMMR_AIFSN, + NL80211_WMMR_TXOP, + + /* keep last */ + __NL80211_WMMR_LAST, + NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1 +}; + /** * enum nl80211_frequency_attr - frequency attributes * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved @@ -3190,6 +3214,9 @@ enum nl80211_band_attr { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations. + * This is a nested attribute that contains the wmm limitation per AC. + * (see &enum nl80211_wmm_rule) * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -3218,6 +3245,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_IR_CONCURRENT, NL80211_FREQUENCY_ATTR_NO_20MHZ, NL80211_FREQUENCY_ATTR_NO_10MHZ, + NL80211_FREQUENCY_ATTR_WMM, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ff28f8feeb09..016d0a1de576 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4,6 +4,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation */ #include @@ -645,7 +646,43 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd); } -static int nl80211_msg_put_channel(struct sk_buff *msg, +static int nl80211_msg_put_wmm_rules(struct sk_buff *msg, + const struct ieee80211_reg_rule *rule) +{ + int j; + struct nlattr *nl_wmm_rules = + nla_nest_start(msg, NL80211_FREQUENCY_ATTR_WMM); + + if (!nl_wmm_rules) + goto nla_put_failure; + + for (j = 0; j < IEEE80211_NUM_ACS; j++) { + struct nlattr *nl_wmm_rule = nla_nest_start(msg, j); + + if (!nl_wmm_rule) + goto nla_put_failure; + + if (nla_put_u16(msg, NL80211_WMMR_CW_MIN, + rule->wmm_rule->client[j].cw_min) || + nla_put_u16(msg, NL80211_WMMR_CW_MAX, + rule->wmm_rule->client[j].cw_max) || + nla_put_u8(msg, NL80211_WMMR_AIFSN, + rule->wmm_rule->client[j].aifsn) || + nla_put_u8(msg, NL80211_WMMR_TXOP, + rule->wmm_rule->client[j].cot)) + goto nla_put_failure; + + nla_nest_end(msg, nl_wmm_rule); + } + nla_nest_end(msg, nl_wmm_rules); + + return 0; + +nla_put_failure: + return -ENOBUFS; +} + +static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, struct ieee80211_channel *chan, bool large) { @@ -721,6 +758,16 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, DBM_TO_MBM(chan->max_power))) goto nla_put_failure; + if (large) { + const struct ieee80211_reg_rule *rule = + freq_reg_info(wiphy, chan->center_freq); + + if (!IS_ERR(rule) && rule->wmm_rule) { + if (nl80211_msg_put_wmm_rules(msg, rule)) + goto nla_put_failure; + } + } + return 0; nla_put_failure: @@ -1631,7 +1678,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, chan = &sband->channels[i]; if (nl80211_msg_put_channel( - msg, chan, + msg, &rdev->wiphy, chan, state->split)) goto nla_put_failure; @@ -14320,7 +14367,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy, nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE); if (!nl_freq) goto nla_put_failure; - if (nl80211_msg_put_channel(msg, channel_before, false)) + + if (nl80211_msg_put_channel(msg, wiphy, channel_before, false)) goto nla_put_failure; nla_nest_end(msg, nl_freq); @@ -14328,7 +14376,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy, nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER); if (!nl_freq) goto nla_put_failure; - if (nl80211_msg_put_channel(msg, channel_after, false)) + + if (nl80211_msg_put_channel(msg, wiphy, channel_after, false)) goto nla_put_failure; nla_nest_end(msg, nl_freq); From aced43ce780dc5e683b3de00ce9fb3db7d28e1d3 Mon Sep 17 00:00:00 2001 From: Amar Singhal Date: Thu, 26 Apr 2018 20:13:07 +0300 Subject: [PATCH 09/30] cfg80211: Call reg_notifier for self managed hints conditionally Currently the regulatory core does not call the regulatory callback reg_notifier for self managed wiphys, but regulatory_hint_user() call is independent of wiphy and is meant for all wiphys in the system. Even a self managed wiphy may be interested in regulatory_hint_user() to know the country code from a trusted regulatory domain change like a cellular base station. Therefore, for the regulatory source NL80211_REGDOM_SET_BY_USER and the user hint type NL80211_USER_REG_HINT_CELL_BASE, call the regulatory notifier. No current wlan driver uses the REGULATORY_WIPHY_SELF_MANAGED flag while also registering the reg_notifier regulatory callback, therefore there will be no impact on existing drivers without them being explicitly modified to take advantage of this new possibility. Signed-off-by: Amar Singhal Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/wireless/reg.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ecfee5f06c76..69cf79165d43 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2768,6 +2768,21 @@ out_free: reg_free_request(reg_request); } +static void notify_self_managed_wiphys(struct regulatory_request *request) +{ + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + wiphy = &rdev->wiphy; + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && + request->initiator == NL80211_REGDOM_SET_BY_USER && + request->user_reg_hint_type == + NL80211_USER_REG_HINT_CELL_BASE) + reg_call_notifier(wiphy, request); + } +} + static bool reg_only_self_managed_wiphys(void) { struct cfg80211_registered_device *rdev; @@ -2819,6 +2834,7 @@ static void reg_process_pending_hints(void) spin_unlock(®_requests_lock); + notify_self_managed_wiphys(reg_request); if (reg_only_self_managed_wiphys()) { reg_free_request(reg_request); return; @@ -3698,17 +3714,26 @@ EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl); void wiphy_regulatory_register(struct wiphy *wiphy) { - struct regulatory_request *lr; + struct regulatory_request *lr = get_last_request(); - /* self-managed devices ignore external hints */ - if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + /* self-managed devices ignore beacon hints and country IE */ + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | REGULATORY_COUNTRY_IE_IGNORE; + /* + * The last request may have been received before this + * registration call. Call the driver notifier if + * initiator is USER and user type is CELL_BASE. + */ + if (lr->initiator == NL80211_REGDOM_SET_BY_USER && + lr->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE) + reg_call_notifier(wiphy, lr); + } + if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint++; - lr = get_last_request(); wiphy_update_regulatory(wiphy, lr->initiator); wiphy_all_share_dfs_chan_state(wiphy); } From 81d5439da84419ee35bea54309a9f2c3871b6605 Mon Sep 17 00:00:00 2001 From: Balaji Pothunoori Date: Mon, 16 Apr 2018 20:18:40 +0530 Subject: [PATCH 10/30] cfg80211: average ack rssi support for data frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Average ack rssi will be given to userspace via NL80211 interface if firmware is capable. Userspace tool ‘iw’ can process this information and give the output as one of the fields in ‘iw dev wlanX station dump’. Example output : localhost ~ #iw dev wlan-5000mhz station dump Station 34:f3:9a:aa:3b:29 (on wlan-5000mhz) inactive time: 5370 ms rx bytes: 85321 rx packets: 576 tx bytes: 14225 tx packets: 71 tx retries: 0 tx failed: 2 beacon loss: 0 rx drop misc: 0 signal: -54 dBm signal avg: -53 dBm tx bitrate: 866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2 rx bitrate: 866.7 MBit/s VHT-MCS 9 80MHz short GI VHT-NSS 2 avg ack signal: -56 dBm authorized: yes authenticated: yes associated: yes preamble: short WMM/WME: yes MFP: no TDLS peer: no DTIM period: 2 beacon interval:100 short preamble: yes short slot time:yes connected time: 203 seconds Main use case is to measure the signal strength of a connected station to AP. Data packet transmit rates and bandwidth used by station can vary a lot even if the station is at fixed location, especially if the rates used are multi stream(2stream, 3stream) rates with different bandwidth(20/40/80 Mhz). These multi stream rates are sensitive and station can use different transmit power for each of the rate and bandwidth combinations. RSSI measured from these RX packets on AP will be not stable and can vary a lot with in a short time. Whereas 802.11 ack frames from station are sent relatively at a constant rate (6/12/24 Mbps) with constant bandwidth(20 Mhz). So average rssi of the ack packets is good and more accurate. Signed-off-by: Balaji Pothunoori Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 7 +++++++ net/wireless/nl80211.c | 3 +++ 3 files changed, 13 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 250dac390806..5e888ec62811 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1152,6 +1152,8 @@ struct cfg80211_tid_stats { * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last * (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs. * @ack_signal: signal strength (in dBm) of the last ACK frame. + * @avg_ack_signal: average rssi value of ack packet for the no of msdu's has + * been sent. */ struct station_info { u64 filled; @@ -1197,6 +1199,7 @@ struct station_info { u8 rx_beacon_signal_avg; struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1]; s8 ack_signal; + s8 avg_ack_signal; }; #if IS_ENABLED(CONFIG_CFG80211) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 04c9b97aa5fc..8e55b63ed3ff 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2981,6 +2981,8 @@ enum nl80211_sta_bss_param { * received from the station (u64, usec) * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) + * @NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG: avg signal strength of (data) + * ACK frame (s8, dBm) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -3020,6 +3022,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_RX_DURATION, NL80211_STA_INFO_PAD, NL80211_STA_INFO_ACK_SIGNAL, + NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -5066,6 +5069,9 @@ enum nl80211_feature_flags { * "radar detected" event. * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and * receiving control port frames over nl80211 instead of the netdevice. + * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack + * rssi if firmware support, this flag is to intimate about ack rssi + * support to nl80211. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -5098,6 +5104,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, NL80211_EXT_FEATURE_DFS_OFFLOAD, NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 016d0a1de576..6b942a68d1c8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4541,6 +4541,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO_U64(BEACON_RX, rx_beacon); PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); PUT_SINFO(ACK_SIGNAL, ack_signal, u8); + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT)) + PUT_SINFO(DATA_ACK_SIGNAL_AVG, avg_ack_signal, s8); #undef PUT_SINFO #undef PUT_SINFO_U64 From cc60dbbfed8ff0bd4c530ee48e9e915333a35470 Mon Sep 17 00:00:00 2001 From: Balaji Pothunoori Date: Mon, 16 Apr 2018 20:18:41 +0530 Subject: [PATCH 11/30] mac80211: average ack rssi support for data frames The driver will process the RSSI if available and send it to mac80211. mac80211 will compute the weighted average of ack RSSI for stations. Signed-off-by: Balaji Pothunoori Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 10 ++++++++++ net/mac80211/sta_info.h | 2 ++ net/mac80211/status.c | 2 ++ 3 files changed, 14 insertions(+) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 2d5ffb0a16f6..f83e6e2ad5ab 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -358,6 +358,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->last_connected = ktime_get_seconds(); ewma_signal_init(&sta->rx_stats_avg.signal); + ewma_avg_signal_init(&sta->status_stats.avg_ack_signal); for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++) ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]); @@ -2294,6 +2295,15 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->ack_signal = sta->status_stats.last_ack_signal; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); } + + if (ieee80211_hw_check(&sta->local->hw, REPORTS_TX_ACK_STATUS) && + !(sinfo->filled & BIT_ULL(NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG))) { + sinfo->avg_ack_signal = + -(s8)ewma_avg_signal_read( + &sta->status_stats.avg_ack_signal); + sinfo->filled |= + BIT_ULL(NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG); + } } u32 sta_get_expected_throughput(struct sta_info *sta) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index f64eb86ca64b..d79bd6eeb549 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -119,6 +119,7 @@ enum ieee80211_sta_info_flags { #define HT_AGG_STATE_START_CB 6 #define HT_AGG_STATE_STOP_CB 7 +DECLARE_EWMA(avg_signal, 10, 8) enum ieee80211_agg_stop_reason { AGG_STOP_DECLINED, AGG_STOP_LOCAL_REQUEST, @@ -550,6 +551,7 @@ struct sta_info { unsigned long last_ack; s8 last_ack_signal; bool ack_signal_filled; + struct ewma_avg_signal avg_ack_signal; } status_stats; /* Updated from TX path only, no locking requirements */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 743e89c5926c..9a6d7208bf4f 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -195,6 +195,8 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) sta->status_stats.last_ack_signal = (s8)txinfo->status.ack_signal; sta->status_stats.ack_signal_filled = true; + ewma_avg_signal_add(&sta->status_stats.avg_ack_signal, + -txinfo->status.ack_signal); } } From 52539ca89f365d3db530535fbffa88a3cca4d2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 8 May 2018 13:03:50 +0200 Subject: [PATCH 12/30] cfg80211: Expose TXQ stats and parameters to userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for exporting the mac80211 TXQ stats via nl80211 by way of a nested TXQ stats attribute, as well as for configuring the quantum and limits that were previously only changeable through debugfs. This commit adds just the nl80211 API, a subsequent commit adds support to mac80211 itself. Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 50 +++++++++ include/uapi/linux/nl80211.h | 58 ++++++++++ net/mac80211/ethtool.c | 32 +++--- net/wireless/nl80211.c | 202 ++++++++++++++++++++++++++++++----- net/wireless/rdev-ops.h | 12 +++ net/wireless/trace.h | 14 +++ net/wireless/wext-compat.c | 23 ++-- 7 files changed, 343 insertions(+), 48 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5e888ec62811..8db6071b6063 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1079,6 +1079,37 @@ struct sta_bss_parameters { u16 beacon_interval; }; +/** + * struct cfg80211_txq_stats - TXQ statistics for this TID + * @filled: bitmap of flags using the bits of &enum nl80211_txq_stats to + * indicate the relevant values in this struct are filled + * @backlog_bytes: total number of bytes currently backlogged + * @backlog_packets: total number of packets currently backlogged + * @flows: number of new flows seen + * @drops: total number of packets dropped + * @ecn_marks: total number of packets marked with ECN CE + * @overlimit: number of drops due to queue space overflow + * @overmemory: number of drops due to memory limit overflow + * @collisions: number of hash collisions + * @tx_bytes: total number of bytes dequeued + * @tx_packets: total number of packets dequeued + * @max_flows: maximum number of flows supported + */ +struct cfg80211_txq_stats { + u32 filled; + u32 backlog_bytes; + u32 backlog_packets; + u32 flows; + u32 drops; + u32 ecn_marks; + u32 overlimit; + u32 overmemory; + u32 collisions; + u32 tx_bytes; + u32 tx_packets; + u32 max_flows; +}; + /** * struct cfg80211_tid_stats - per-TID statistics * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to @@ -1088,6 +1119,7 @@ struct sta_bss_parameters { * @tx_msdu_retries: number of retries (not counting the first) for * transmitted MSDUs * @tx_msdu_failed: number of failed transmitted MSDUs + * @txq_stats: TXQ statistics */ struct cfg80211_tid_stats { u32 filled; @@ -1095,6 +1127,7 @@ struct cfg80211_tid_stats { u64 tx_msdu; u64 tx_msdu_retries; u64 tx_msdu_failed; + struct cfg80211_txq_stats txq_stats; }; #define IEEE80211_MAX_CHAINS 4 @@ -2204,6 +2237,9 @@ enum cfg80211_connect_params_changed { * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed * @WIPHY_PARAM_DYN_ACK: dynack has been enabled + * @WIPHY_PARAM_TXQ_LIMIT: TXQ packet limit has been changed + * @WIPHY_PARAM_TXQ_MEMORY_LIMIT: TXQ memory limit has been changed + * @WIPHY_PARAM_TXQ_QUANTUM: TXQ scheduler quantum */ enum wiphy_params_flags { WIPHY_PARAM_RETRY_SHORT = 1 << 0, @@ -2212,6 +2248,9 @@ enum wiphy_params_flags { WIPHY_PARAM_RTS_THRESHOLD = 1 << 3, WIPHY_PARAM_COVERAGE_CLASS = 1 << 4, WIPHY_PARAM_DYN_ACK = 1 << 5, + WIPHY_PARAM_TXQ_LIMIT = 1 << 6, + WIPHY_PARAM_TXQ_MEMORY_LIMIT = 1 << 7, + WIPHY_PARAM_TXQ_QUANTUM = 1 << 8, }; /** @@ -2964,6 +3003,9 @@ struct cfg80211_external_auth_params { * * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * + * @get_txq_stats: Get TXQ stats for interface or phy. If wdev is %NULL, this + * function should return phy stats, and interface stats otherwise. + * * @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake. * If not deleted through @del_pmk the PMK remains valid until disconnect * upon which the driver should clear it. @@ -3265,6 +3307,10 @@ struct cfg80211_ops { struct net_device *dev, const bool enabled); + int (*get_txq_stats)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_txq_stats *txqstats); + int (*set_pmk)(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_pmk_conf *conf); int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev, @@ -3943,6 +3989,10 @@ struct wiphy { u8 nan_supported_bands; + u32 txq_limit; + u32 txq_memory_limit; + u32 txq_quantum; + char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8e55b63ed3ff..5e67e3444aba 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2226,6 +2226,16 @@ enum nl80211_commands { * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. * + * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum + * nl80211_txq_stats) + * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy. + * The smaller of this and the memory limit is enforced. + * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the + * TXQ queues for this phy. The smaller of this and the packet limit is + * enforced. + * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes + * a flow is assigned on each round of the DRR scheduler. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2660,6 +2670,11 @@ enum nl80211_attrs { NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + NL80211_ATTR_TXQ_STATS, + NL80211_ATTR_TXQ_LIMIT, + NL80211_ATTR_TXQ_MEMORY_LIMIT, + NL80211_ATTR_TXQ_QUANTUM, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3040,6 +3055,7 @@ enum nl80211_sta_info { * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted * MSDUs (u64) * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute) * @NUM_NL80211_TID_STATS: number of attributes here * @NL80211_TID_STATS_MAX: highest numbered attribute here */ @@ -3050,12 +3066,51 @@ enum nl80211_tid_stats { NL80211_TID_STATS_TX_MSDU_RETRIES, NL80211_TID_STATS_TX_MSDU_FAILED, NL80211_TID_STATS_PAD, + NL80211_TID_STATS_TXQ_STATS, /* keep last */ NUM_NL80211_TID_STATS, NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1 }; +/** + * enum nl80211_txq_stats - per TXQ statistics attributes + * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved + * @NUM_NL80211_TXQ_STATS: number of attributes here + * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged + * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently + * backlogged + * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen + * @NL80211_TXQ_STATS_DROPS: total number of packet drops + * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks + * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow + * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow + * (only for per-phy stats) + * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions + * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ + * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ + * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY + * @NL80211_TXQ_STATS_MAX: highest numbered attribute here + */ +enum nl80211_txq_stats { + __NL80211_TXQ_STATS_INVALID, + NL80211_TXQ_STATS_BACKLOG_BYTES, + NL80211_TXQ_STATS_BACKLOG_PACKETS, + NL80211_TXQ_STATS_FLOWS, + NL80211_TXQ_STATS_DROPS, + NL80211_TXQ_STATS_ECN_MARKS, + NL80211_TXQ_STATS_OVERLIMIT, + NL80211_TXQ_STATS_OVERMEMORY, + NL80211_TXQ_STATS_COLLISIONS, + NL80211_TXQ_STATS_TX_BYTES, + NL80211_TXQ_STATS_TX_PACKETS, + NL80211_TXQ_STATS_MAX_FLOWS, + + /* keep last */ + NUM_NL80211_TXQ_STATS, + NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1 +}; + /** * enum nl80211_mpath_flags - nl80211 mesh path flags * @@ -5072,6 +5127,8 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack * rssi if firmware support, this flag is to intimate about ack rssi * support to nl80211. + * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate + * TXQs. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -5105,6 +5162,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_DFS_OFFLOAD, NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT, + NL80211_EXT_FEATURE_TXQS, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 08408520c3f8..1afeff94af8b 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -71,11 +71,15 @@ static void ieee80211_get_stats(struct net_device *dev, struct ieee80211_channel *channel; struct sta_info *sta; struct ieee80211_local *local = sdata->local; - struct station_info sinfo; + struct station_info *sinfo; struct survey_info survey; int i, q; #define STA_STATS_SURVEY_LEN 7 + sinfo = kmalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return; + memset(data, 0, sizeof(u64) * STA_STATS_LEN); #define ADD_STA_STATS(sta) \ @@ -86,8 +90,8 @@ static void ieee80211_get_stats(struct net_device *dev, data[i++] += sta->rx_stats.fragments; \ data[i++] += sta->rx_stats.dropped; \ \ - data[i++] += sinfo.tx_packets; \ - data[i++] += sinfo.tx_bytes; \ + data[i++] += sinfo->tx_packets; \ + data[i++] += sinfo->tx_bytes; \ data[i++] += sta->status_stats.filtered; \ data[i++] += sta->status_stats.retry_failed; \ data[i++] += sta->status_stats.retry_count; \ @@ -107,8 +111,8 @@ static void ieee80211_get_stats(struct net_device *dev, if (!(sta && !WARN_ON(sta->sdata->dev != dev))) goto do_survey; - memset(&sinfo, 0, sizeof(sinfo)); - sta_set_sinfo(sta, &sinfo); + memset(sinfo, 0, sizeof(*sinfo)); + sta_set_sinfo(sta, sinfo); i = 0; ADD_STA_STATS(sta); @@ -116,17 +120,17 @@ static void ieee80211_get_stats(struct net_device *dev, data[i++] = sta->sta_state; - if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) + if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) data[i] = 100000 * - cfg80211_calculate_bitrate(&sinfo.txrate); + cfg80211_calculate_bitrate(&sinfo->txrate); i++; - if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE)) + if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) data[i] = 100000 * - cfg80211_calculate_bitrate(&sinfo.rxrate); + cfg80211_calculate_bitrate(&sinfo->rxrate); i++; - if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) - data[i] = (u8)sinfo.signal_avg; + if (sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) + data[i] = (u8)sinfo->signal_avg; i++; } else { list_for_each_entry(sta, &local->sta_list, list) { @@ -134,14 +138,16 @@ static void ieee80211_get_stats(struct net_device *dev, if (sta->sdata->dev != dev) continue; - memset(&sinfo, 0, sizeof(sinfo)); - sta_set_sinfo(sta, &sinfo); + memset(sinfo, 0, sizeof(*sinfo)); + sta_set_sinfo(sta, sinfo); i = 0; ADD_STA_STATS(sta); } } do_survey: + kfree(sinfo); + i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; /* Get survey stats for current channel */ survey.filled = 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6b942a68d1c8..f7715b85fd2b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -424,6 +424,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG }, + + [NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 }, + [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 }, + [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -774,6 +778,39 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, return -ENOBUFS; } +static bool nl80211_put_txq_stats(struct sk_buff *msg, + struct cfg80211_txq_stats *txqstats, + int attrtype) +{ + struct nlattr *txqattr; + +#define PUT_TXQVAL_U32(attr, memb) do { \ + if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) && \ + nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \ + return false; \ + } while (0) + + txqattr = nla_nest_start(msg, attrtype); + if (!txqattr) + return false; + + PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes); + PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets); + PUT_TXQVAL_U32(FLOWS, flows); + PUT_TXQVAL_U32(DROPS, drops); + PUT_TXQVAL_U32(ECN_MARKS, ecn_marks); + PUT_TXQVAL_U32(OVERLIMIT, overlimit); + PUT_TXQVAL_U32(OVERMEMORY, overmemory); + PUT_TXQVAL_U32(COLLISIONS, collisions); + PUT_TXQVAL_U32(TX_BYTES, tx_bytes); + PUT_TXQVAL_U32(TX_PACKETS, tx_packets); + PUT_TXQVAL_U32(MAX_FLOWS, max_flows); + nla_nest_end(msg, txqattr); + +#undef PUT_TXQVAL_U32 + return true; +} + /* netlink command implementations */ struct key_parse { @@ -1973,6 +2010,28 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.nan_supported_bands)) goto nla_put_failure; + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_TXQS)) { + struct cfg80211_txq_stats txqstats = {}; + int res; + + res = rdev_get_txq_stats(rdev, NULL, &txqstats); + if (!res && + !nl80211_put_txq_stats(msg, &txqstats, + NL80211_ATTR_TXQ_STATS)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_TXQ_LIMIT, + rdev->wiphy.txq_limit)) + goto nla_put_failure; + if (nla_put_u32(msg, NL80211_ATTR_TXQ_MEMORY_LIMIT, + rdev->wiphy.txq_memory_limit)) + goto nla_put_failure; + if (nla_put_u32(msg, NL80211_ATTR_TXQ_QUANTUM, + rdev->wiphy.txq_quantum)) + goto nla_put_failure; + } + /* done */ state->split_start = 0; break; @@ -2350,6 +2409,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) u8 retry_short = 0, retry_long = 0; u32 frag_threshold = 0, rts_threshold = 0; u8 coverage_class = 0; + u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0; ASSERT_RTNL(); @@ -2556,10 +2616,38 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) changed |= WIPHY_PARAM_DYN_ACK; } + if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_TXQS)) + return -EOPNOTSUPP; + txq_limit = nla_get_u32( + info->attrs[NL80211_ATTR_TXQ_LIMIT]); + changed |= WIPHY_PARAM_TXQ_LIMIT; + } + + if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_TXQS)) + return -EOPNOTSUPP; + txq_memory_limit = nla_get_u32( + info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]); + changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT; + } + + if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_TXQS)) + return -EOPNOTSUPP; + txq_quantum = nla_get_u32( + info->attrs[NL80211_ATTR_TXQ_QUANTUM]); + changed |= WIPHY_PARAM_TXQ_QUANTUM; + } + if (changed) { u8 old_retry_short, old_retry_long; u32 old_frag_threshold, old_rts_threshold; u8 old_coverage_class; + u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum; if (!rdev->ops->set_wiphy_params) return -EOPNOTSUPP; @@ -2569,6 +2657,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) old_frag_threshold = rdev->wiphy.frag_threshold; old_rts_threshold = rdev->wiphy.rts_threshold; old_coverage_class = rdev->wiphy.coverage_class; + old_txq_limit = rdev->wiphy.txq_limit; + old_txq_memory_limit = rdev->wiphy.txq_memory_limit; + old_txq_quantum = rdev->wiphy.txq_quantum; if (changed & WIPHY_PARAM_RETRY_SHORT) rdev->wiphy.retry_short = retry_short; @@ -2580,6 +2671,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rdev->wiphy.rts_threshold = rts_threshold; if (changed & WIPHY_PARAM_COVERAGE_CLASS) rdev->wiphy.coverage_class = coverage_class; + if (changed & WIPHY_PARAM_TXQ_LIMIT) + rdev->wiphy.txq_limit = txq_limit; + if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT) + rdev->wiphy.txq_memory_limit = txq_memory_limit; + if (changed & WIPHY_PARAM_TXQ_QUANTUM) + rdev->wiphy.txq_quantum = txq_quantum; result = rdev_set_wiphy_params(rdev, changed); if (result) { @@ -2588,6 +2685,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rdev->wiphy.frag_threshold = old_frag_threshold; rdev->wiphy.rts_threshold = old_rts_threshold; rdev->wiphy.coverage_class = old_coverage_class; + rdev->wiphy.txq_limit = old_txq_limit; + rdev->wiphy.txq_memory_limit = old_txq_memory_limit; + rdev->wiphy.txq_quantum = old_txq_quantum; return result; } } @@ -2709,6 +2809,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag } wdev_unlock(wdev); + if (rdev->ops->get_txq_stats) { + struct cfg80211_txq_stats txqstats = {}; + int ret = rdev_get_txq_stats(rdev, wdev, &txqstats); + + if (ret == 0 && + !nl80211_put_txq_stats(msg, &txqstats, + NL80211_ATTR_TXQ_STATS)) + goto nla_put_failure; + } + genlmsg_end(msg, hdr); return 0; @@ -4582,6 +4692,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed); #undef PUT_TIDVAL_U64 + if ((tidstats->filled & + BIT(NL80211_TID_STATS_TXQ_STATS)) && + !nl80211_put_txq_stats(msg, &tidstats->txq_stats, + NL80211_TID_STATS_TXQ_STATS)) + goto nla_put_failure; + nla_nest_end(msg, tidattr); } @@ -4606,13 +4722,17 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, static int nl80211_dump_station(struct sk_buff *skb, struct netlink_callback *cb) { - struct station_info sinfo; + struct station_info *sinfo; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; u8 mac_addr[ETH_ALEN]; int sta_idx = cb->args[2]; int err; + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) @@ -4629,9 +4749,9 @@ static int nl80211_dump_station(struct sk_buff *skb, } while (1) { - memset(&sinfo, 0, sizeof(sinfo)); + memset(sinfo, 0, sizeof(*sinfo)); err = rdev_dump_station(rdev, wdev->netdev, sta_idx, - mac_addr, &sinfo); + mac_addr, sinfo); if (err == -ENOENT) break; if (err) @@ -4641,7 +4761,7 @@ static int nl80211_dump_station(struct sk_buff *skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev->netdev, mac_addr, - &sinfo) < 0) + sinfo) < 0) goto out; sta_idx++; @@ -4652,6 +4772,7 @@ static int nl80211_dump_station(struct sk_buff *skb, err = skb->len; out_err: rtnl_unlock(); + kfree(sinfo); return err; } @@ -4660,37 +4781,49 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - struct station_info sinfo; + struct station_info *sinfo; struct sk_buff *msg; u8 *mac_addr = NULL; int err; - memset(&sinfo, 0, sizeof(sinfo)); + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; + if (!info->attrs[NL80211_ATTR_MAC]) { + err = -EINVAL; + goto out; + } mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (!rdev->ops->get_station) - return -EOPNOTSUPP; + if (!rdev->ops->get_station) { + err = -EOPNOTSUPP; + goto out; + } - err = rdev_get_station(rdev, dev, mac_addr, &sinfo); + err = rdev_get_station(rdev, dev, mac_addr, sinfo); if (err) - return err; + goto out; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; + if (!msg) { + err = -ENOMEM; + goto out; + } if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, info->snd_portid, info->snd_seq, 0, - rdev, dev, mac_addr, &sinfo) < 0) { + rdev, dev, mac_addr, sinfo) < 0) { nlmsg_free(msg); - return -ENOBUFS; + err = -ENOBUFS; + goto out; } - return genlmsg_reply(msg, info); + err = genlmsg_reply(msg, info); +out: + kfree(sinfo); + return err; } int cfg80211_check_station_change(struct wiphy *wiphy, @@ -9954,18 +10087,26 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, */ if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss && rdev->ops->get_station) { - struct station_info sinfo = {}; + struct station_info *sinfo; u8 *mac_addr; + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + mac_addr = wdev->current_bss->pub.bssid; - err = rdev_get_station(rdev, dev, mac_addr, &sinfo); - if (err) + err = rdev_get_station(rdev, dev, mac_addr, sinfo); + if (err) { + kfree(sinfo); return err; + } - if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) + if (sinfo->filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) wdev->cqm_config->last_rssi_event_value = - (s8) sinfo.rx_beacon_signal_avg; + (s8)sinfo->rx_beacon_signal_avg; + + kfree(sinfo); } last = wdev->cqm_config->last_rssi_event_value; @@ -14499,25 +14640,32 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct sk_buff *msg; - struct station_info empty_sinfo = {}; + struct station_info *empty_sinfo = NULL; - if (!sinfo) - sinfo = &empty_sinfo; + if (!sinfo) { + empty_sinfo = kzalloc(sizeof(*empty_sinfo), GFP_KERNEL); + if (!empty_sinfo) + return; + sinfo = empty_sinfo; + } trace_cfg80211_del_sta(dev, mac_addr); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) - return; + goto out; if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, rdev, dev, mac_addr, sinfo) < 0) { nlmsg_free(msg); - return; + goto out; } genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, NL80211_MCGRP_MLME, gfp); + +out: + kfree(empty_sinfo); } EXPORT_SYMBOL(cfg80211_del_sta_sinfo); diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 87479a53411b..364f5d67f05b 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -586,6 +586,18 @@ rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_get_txq_stats(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_txq_stats *txqstats) +{ + int ret; + trace_rdev_get_txq_stats(&rdev->wiphy, wdev); + ret = rdev->ops->get_txq_stats(&rdev->wiphy, wdev, txqstats); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) { trace_rdev_rfkill_poll(&rdev->wiphy); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 55fb279a5196..2b417a2fe63f 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3243,6 +3243,20 @@ TRACE_EVENT(rdev_set_multicast_to_unicast, WIPHY_PR_ARG, NETDEV_PR_ARG, BOOL_TO_STR(__entry->enabled)) ); + +TRACE_EVENT(rdev_get_txq_stats, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 05186a47878f..9e002df0f8d8 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1254,7 +1254,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct station_info sinfo = {}; + struct station_info *sinfo; u8 addr[ETH_ALEN]; int err; @@ -1274,16 +1274,23 @@ static int cfg80211_wext_giwrate(struct net_device *dev, if (err) return err; - err = rdev_get_station(rdev, dev, addr, &sinfo); + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + + err = rdev_get_station(rdev, dev, addr, sinfo); if (err) - return err; + goto out; - if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))) - return -EOPNOTSUPP; + if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) { + err = -EOPNOTSUPP; + goto out; + } - rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); - - return 0; + rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo->txrate); +out: + kfree(sinfo); + return err; } /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ From 2fe4a29a452a68ffa8a501000d0ef8095c242eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 8 May 2018 13:03:50 +0200 Subject: [PATCH 13/30] mac80211: Support the new cfg80211 TXQ stats API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support to mac80211 to export TXQ stats via the newly added cfg80211 API. Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 99 ++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 3 ++ net/mac80211/main.c | 3 ++ net/mac80211/sta_info.c | 12 +++++ net/mac80211/tx.c | 20 ++++++++ 5 files changed, 137 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 85dbaa891059..5ce9d121af2b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2376,6 +2376,11 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG)) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS); + if (changed & (WIPHY_PARAM_TXQ_LIMIT | + WIPHY_PARAM_TXQ_MEMORY_LIMIT | + WIPHY_PARAM_TXQ_QUANTUM)) + ieee80211_txq_set_params(local); + return 0; } @@ -3705,6 +3710,99 @@ static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy, return 0; } +void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats, + struct txq_info *txqi) +{ + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_BYTES))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_BYTES); + txqstats->backlog_bytes = txqi->tin.backlog_bytes; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS); + txqstats->backlog_packets = txqi->tin.backlog_packets; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_FLOWS))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_FLOWS); + txqstats->flows = txqi->tin.flows; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_DROPS))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_DROPS); + txqstats->drops = txqi->cstats.drop_count; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_ECN_MARKS))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_ECN_MARKS); + txqstats->ecn_marks = txqi->cstats.ecn_mark; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_OVERLIMIT))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_OVERLIMIT); + txqstats->overlimit = txqi->tin.overlimit; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_COLLISIONS))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_COLLISIONS); + txqstats->collisions = txqi->tin.collisions; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_BYTES))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_BYTES); + txqstats->tx_bytes = txqi->tin.tx_bytes; + } + + if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_PACKETS))) { + txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_PACKETS); + txqstats->tx_packets = txqi->tin.tx_packets; + } +} + +static int ieee80211_get_txq_stats(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_txq_stats *txqstats) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata; + int ret = 0; + + if (!local->ops->wake_tx_queue) + return 1; + + spin_lock_bh(&local->fq.lock); + rcu_read_lock(); + + if (wdev) { + sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + if (!sdata->vif.txq) { + ret = 1; + goto out; + } + ieee80211_fill_txq_stats(txqstats, to_txq_info(sdata->vif.txq)); + } else { + /* phy stats */ + txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS) | + BIT(NL80211_TXQ_STATS_BACKLOG_BYTES) | + BIT(NL80211_TXQ_STATS_OVERLIMIT) | + BIT(NL80211_TXQ_STATS_OVERMEMORY) | + BIT(NL80211_TXQ_STATS_COLLISIONS) | + BIT(NL80211_TXQ_STATS_MAX_FLOWS); + txqstats->backlog_packets = local->fq.backlog; + txqstats->backlog_bytes = local->fq.memory_usage; + txqstats->overlimit = local->fq.overlimit; + txqstats->overmemory = local->fq.overmemory; + txqstats->collisions = local->fq.collisions; + txqstats->max_flows = local->fq.flows_cnt; + } + +out: + rcu_read_unlock(); + spin_unlock_bh(&local->fq.lock); + + return ret; +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -3798,4 +3896,5 @@ const struct cfg80211_ops mac80211_config_ops = { .del_nan_func = ieee80211_del_nan_func, .set_multicast_to_unicast = ieee80211_set_multicast_to_unicast, .tx_control_port = ieee80211_tx_control_port, + .get_txq_stats = ieee80211_get_txq_stats, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6372dbdadf53..d1978aa1c15d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2012,6 +2012,7 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local) } int ieee80211_txq_setup_flows(struct ieee80211_local *local); +void ieee80211_txq_set_params(struct ieee80211_local *local); void ieee80211_txq_teardown_flows(struct ieee80211_local *local); void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, @@ -2020,6 +2021,8 @@ void ieee80211_txq_purge(struct ieee80211_local *local, struct txq_info *txqi); void ieee80211_txq_remove_vlan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); +void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats, + struct txq_info *txqi); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, const u8 *extra, size_t extra_len, const u8 *bssid, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 9ea17afaa237..4d2e797e3f16 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -565,6 +565,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + if (ops->wake_tx_queue) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM); wiphy->bss_priv_size = sizeof(struct ieee80211_bss); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f83e6e2ad5ab..43f34aa873bc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2052,6 +2052,18 @@ static void sta_set_tidstats(struct sta_info *sta, tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_FAILED); tidstats->tx_msdu_failed = sta->status_stats.msdu_failed[tid]; } + + if (local->ops->wake_tx_queue && tid < IEEE80211_NUM_TIDS) { + spin_lock_bh(&local->fq.lock); + rcu_read_lock(); + + tidstats->filled |= BIT(NL80211_TID_STATS_TXQ_STATS); + ieee80211_fill_txq_stats(&tidstats->txq_stats, + to_txq_info(sta->sta.txq[tid])); + + rcu_read_unlock(); + spin_unlock_bh(&local->fq.lock); + } } static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 062e125a324c..8275a58450b2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1459,6 +1459,24 @@ void ieee80211_txq_purge(struct ieee80211_local *local, ieee80211_purge_tx_queue(&local->hw, &txqi->frags); } +void ieee80211_txq_set_params(struct ieee80211_local *local) +{ + if (local->hw.wiphy->txq_limit) + local->fq.limit = local->hw.wiphy->txq_limit; + else + local->hw.wiphy->txq_limit = local->fq.limit; + + if (local->hw.wiphy->txq_memory_limit) + local->fq.memory_limit = local->hw.wiphy->txq_memory_limit; + else + local->hw.wiphy->txq_memory_limit = local->fq.memory_limit; + + if (local->hw.wiphy->txq_quantum) + local->fq.quantum = local->hw.wiphy->txq_quantum; + else + local->hw.wiphy->txq_quantum = local->fq.quantum; +} + int ieee80211_txq_setup_flows(struct ieee80211_local *local) { struct fq *fq = &local->fq; @@ -1508,6 +1526,8 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local) for (i = 0; i < fq->flows_cnt; i++) codel_vars_init(&local->cvars[i]); + ieee80211_txq_set_params(local); + return 0; } From 57c6cb81717f957fb741f2e4c79bd0e2f4f55910 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 8 May 2018 13:57:32 +0100 Subject: [PATCH 14/30] mac80211: ethtool: avoid 32 bit multiplication overflow The multiplication of 100000 * cfg80211_calculate_bitrate() is a 32 bit operation and can overflow if cfg80211_calculate_bitrate is greater than 42949. Although I don't believe this is occurring at present, it would be safer to avoid the potential overflow by making the constant 100000 an ULL to ensure a 64 multiplication occurs. Detected by CoverityScan, CID#1468643 ("Unintentional integer overflow") Signed-off-by: Colin Ian King Signed-off-by: Johannes Berg --- net/mac80211/ethtool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 1afeff94af8b..09210aa8ea9a 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -121,11 +121,11 @@ static void ieee80211_get_stats(struct net_device *dev, if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) - data[i] = 100000 * + data[i] = 100000ULL * cfg80211_calculate_bitrate(&sinfo->txrate); i++; if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) - data[i] = 100000 * + data[i] = 100000ULL * cfg80211_calculate_bitrate(&sinfo->rxrate); i++; From 8689c051a20195b228e19acb155c7d6e48a86753 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 10 May 2018 13:50:12 +0200 Subject: [PATCH 15/30] cfg80211: dynamically allocate per-tid stats for station info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the addition of TXQ stats in the per-tid statistics the struct station_info grew significantly. This resulted in stack size warnings due to the structure itself being above the limit for the warnings. Add an allocation function that those who want to provide per-tid stats should use to allocate the tid array, i.e. struct station_info::pertid. Cc: Toke Høiland-Jørgensen Fixes: 52539ca89f36 ("cfg80211: Expose TXQ stats and parameters to userspace") Signed-off-by: Arend van Spriel [johannes: fix missing BIT() and logic by removing] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 10 +++++++++- net/mac80211/sta_info.c | 9 +++++---- net/wireless/nl80211.c | 3 ++- net/wireless/util.c | 11 +++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8db6071b6063..8984d24d68b7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1184,6 +1184,7 @@ struct cfg80211_tid_stats { * @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer * @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last * (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs. + * Note that this doesn't use the @filled bit, but is used if non-NULL. * @ack_signal: signal strength (in dBm) of the last ACK frame. * @avg_ack_signal: average rssi value of ack packet for the no of msdu's has * been sent. @@ -1230,7 +1231,7 @@ struct station_info { u64 rx_beacon; u64 rx_duration; u8 rx_beacon_signal_avg; - struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1]; + struct cfg80211_tid_stats *pertid; s8 ack_signal; s8 avg_ack_signal; }; @@ -5701,6 +5702,13 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, gfp_t gfp); +/** + * cfg80211_sinfo_alloc_tid_stats - allocate per-tid statistics. + * + * @sinfo: the station information + * @gfp: allocation flags + */ +int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp); /** * cfg80211_new_sta - notify userspace about station diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 43f34aa873bc..04d47689b557 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2233,11 +2233,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); } - sinfo->filled |= BIT(NL80211_STA_INFO_TID_STATS); - for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { - struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; + if (!cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) { + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { + struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; - sta_set_tidstats(sta, tidstats, i); + sta_set_tidstats(sta, tidstats, i); + } } if (ieee80211_vif_is_mesh(&sdata->vif)) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f7715b85fd2b..3d638f11edb5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4658,7 +4658,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, #undef PUT_SINFO #undef PUT_SINFO_U64 - if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) { + if (sinfo->pertid) { struct nlattr *tidsattr; int tid; @@ -4702,6 +4702,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, } nla_nest_end(msg, tidsattr); + kfree(sinfo->pertid); } nla_nest_end(msg, sinfoattr); diff --git a/net/wireless/util.c b/net/wireless/util.c index d112e9a89364..b5bb1c309914 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1787,6 +1787,17 @@ bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range, return false; } +int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp) +{ + sinfo->pertid = kcalloc(sizeof(*(sinfo->pertid)), + IEEE80211_NUM_TIDS + 1, gfp); + if (!sinfo->pertid) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(cfg80211_sinfo_alloc_tid_stats); + /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ const unsigned char rfc1042_header[] __aligned(2) = From 73887fd906bb77a974fa02663df9937d0aff053a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 May 2018 09:57:55 +0200 Subject: [PATCH 16/30] cfg80211/mac80211: revert to stack allocation for sinfo Arend's previous patch made the sinfo structure smaller again by to dynamically allocating the per-tid stats only when needed. Thus, revert to stack allocation for the struct to simplify the code. Signed-off-by: Johannes Berg --- net/mac80211/ethtool.c | 32 ++++++-------- net/wireless/nl80211.c | 86 ++++++++++++-------------------------- net/wireless/wext-compat.c | 23 ++++------ 3 files changed, 48 insertions(+), 93 deletions(-) diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 09210aa8ea9a..2ba5686cbcab 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -71,15 +71,11 @@ static void ieee80211_get_stats(struct net_device *dev, struct ieee80211_channel *channel; struct sta_info *sta; struct ieee80211_local *local = sdata->local; - struct station_info *sinfo; + struct station_info sinfo; struct survey_info survey; int i, q; #define STA_STATS_SURVEY_LEN 7 - sinfo = kmalloc(sizeof(*sinfo), GFP_KERNEL); - if (!sinfo) - return; - memset(data, 0, sizeof(u64) * STA_STATS_LEN); #define ADD_STA_STATS(sta) \ @@ -90,8 +86,8 @@ static void ieee80211_get_stats(struct net_device *dev, data[i++] += sta->rx_stats.fragments; \ data[i++] += sta->rx_stats.dropped; \ \ - data[i++] += sinfo->tx_packets; \ - data[i++] += sinfo->tx_bytes; \ + data[i++] += sinfo.tx_packets; \ + data[i++] += sinfo.tx_bytes; \ data[i++] += sta->status_stats.filtered; \ data[i++] += sta->status_stats.retry_failed; \ data[i++] += sta->status_stats.retry_count; \ @@ -111,8 +107,8 @@ static void ieee80211_get_stats(struct net_device *dev, if (!(sta && !WARN_ON(sta->sdata->dev != dev))) goto do_survey; - memset(sinfo, 0, sizeof(*sinfo)); - sta_set_sinfo(sta, sinfo); + memset(&sinfo, 0, sizeof(sinfo)); + sta_set_sinfo(sta, &sinfo); i = 0; ADD_STA_STATS(sta); @@ -120,17 +116,17 @@ static void ieee80211_get_stats(struct net_device *dev, data[i++] = sta->sta_state; - if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) + if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) data[i] = 100000ULL * - cfg80211_calculate_bitrate(&sinfo->txrate); + cfg80211_calculate_bitrate(&sinfo.txrate); i++; - if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) + if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE)) data[i] = 100000ULL * - cfg80211_calculate_bitrate(&sinfo->rxrate); + cfg80211_calculate_bitrate(&sinfo.rxrate); i++; - if (sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) - data[i] = (u8)sinfo->signal_avg; + if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) + data[i] = (u8)sinfo.signal_avg; i++; } else { list_for_each_entry(sta, &local->sta_list, list) { @@ -138,16 +134,14 @@ static void ieee80211_get_stats(struct net_device *dev, if (sta->sdata->dev != dev) continue; - memset(sinfo, 0, sizeof(*sinfo)); - sta_set_sinfo(sta, sinfo); + memset(&sinfo, 0, sizeof(sinfo)); + sta_set_sinfo(sta, &sinfo); i = 0; ADD_STA_STATS(sta); } } do_survey: - kfree(sinfo); - i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; /* Get survey stats for current channel */ survey.filled = 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3d638f11edb5..7daceb1f253d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4723,17 +4723,13 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, static int nl80211_dump_station(struct sk_buff *skb, struct netlink_callback *cb) { - struct station_info *sinfo; + struct station_info sinfo; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; u8 mac_addr[ETH_ALEN]; int sta_idx = cb->args[2]; int err; - sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); - if (!sinfo) - return -ENOMEM; - rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) @@ -4750,9 +4746,9 @@ static int nl80211_dump_station(struct sk_buff *skb, } while (1) { - memset(sinfo, 0, sizeof(*sinfo)); + memset(&sinfo, 0, sizeof(sinfo)); err = rdev_dump_station(rdev, wdev->netdev, sta_idx, - mac_addr, sinfo); + mac_addr, &sinfo); if (err == -ENOENT) break; if (err) @@ -4762,7 +4758,7 @@ static int nl80211_dump_station(struct sk_buff *skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev->netdev, mac_addr, - sinfo) < 0) + &sinfo) < 0) goto out; sta_idx++; @@ -4773,7 +4769,6 @@ static int nl80211_dump_station(struct sk_buff *skb, err = skb->len; out_err: rtnl_unlock(); - kfree(sinfo); return err; } @@ -4782,49 +4777,37 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - struct station_info *sinfo; + struct station_info sinfo; struct sk_buff *msg; u8 *mac_addr = NULL; int err; - sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); - if (!sinfo) - return -ENOMEM; + memset(&sinfo, 0, sizeof(sinfo)); - if (!info->attrs[NL80211_ATTR_MAC]) { - err = -EINVAL; - goto out; - } + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (!rdev->ops->get_station) { - err = -EOPNOTSUPP; - goto out; - } + if (!rdev->ops->get_station) + return -EOPNOTSUPP; - err = rdev_get_station(rdev, dev, mac_addr, sinfo); + err = rdev_get_station(rdev, dev, mac_addr, &sinfo); if (err) - goto out; + return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) { - err = -ENOMEM; - goto out; - } + if (!msg) + return -ENOMEM; if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, info->snd_portid, info->snd_seq, 0, - rdev, dev, mac_addr, sinfo) < 0) { + rdev, dev, mac_addr, &sinfo) < 0) { nlmsg_free(msg); - err = -ENOBUFS; - goto out; + return -ENOBUFS; } - err = genlmsg_reply(msg, info); -out: - kfree(sinfo); - return err; + return genlmsg_reply(msg, info); } int cfg80211_check_station_change(struct wiphy *wiphy, @@ -10088,26 +10071,18 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, */ if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss && rdev->ops->get_station) { - struct station_info *sinfo; + struct station_info sinfo = {}; u8 *mac_addr; - sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); - if (!sinfo) - return -ENOMEM; - mac_addr = wdev->current_bss->pub.bssid; - err = rdev_get_station(rdev, dev, mac_addr, sinfo); - if (err) { - kfree(sinfo); + err = rdev_get_station(rdev, dev, mac_addr, &sinfo); + if (err) return err; - } - if (sinfo->filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) + if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) wdev->cqm_config->last_rssi_event_value = - (s8)sinfo->rx_beacon_signal_avg; - - kfree(sinfo); + (s8) sinfo.rx_beacon_signal_avg; } last = wdev->cqm_config->last_rssi_event_value; @@ -14641,32 +14616,25 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct sk_buff *msg; - struct station_info *empty_sinfo = NULL; + struct station_info empty_sinfo = {}; - if (!sinfo) { - empty_sinfo = kzalloc(sizeof(*empty_sinfo), GFP_KERNEL); - if (!empty_sinfo) - return; - sinfo = empty_sinfo; - } + if (!sinfo) + sinfo = &empty_sinfo; trace_cfg80211_del_sta(dev, mac_addr); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) - goto out; + return; if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, rdev, dev, mac_addr, sinfo) < 0) { nlmsg_free(msg); - goto out; + return; } genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, NL80211_MCGRP_MLME, gfp); - -out: - kfree(empty_sinfo); } EXPORT_SYMBOL(cfg80211_del_sta_sinfo); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 9e002df0f8d8..05186a47878f 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1254,7 +1254,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct station_info *sinfo; + struct station_info sinfo = {}; u8 addr[ETH_ALEN]; int err; @@ -1274,23 +1274,16 @@ static int cfg80211_wext_giwrate(struct net_device *dev, if (err) return err; - sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); - if (!sinfo) - return -ENOMEM; - - err = rdev_get_station(rdev, dev, addr, sinfo); + err = rdev_get_station(rdev, dev, addr, &sinfo); if (err) - goto out; + return err; - if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) { - err = -EOPNOTSUPP; - goto out; - } + if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))) + return -EOPNOTSUPP; - rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo->txrate); -out: - kfree(sinfo); - return err; + rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); + + return 0; } /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ From 4a22b00b288649f6d697b350ec606574479a2df3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 11 May 2018 14:25:42 +0100 Subject: [PATCH 17/30] cfg80211: fix spelling mistake: "uknown" -> "unknown" Trivial fix to spelling mistake in pr_debug message text Signed-off-by: Colin Ian King Signed-off-by: Johannes Berg --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 69cf79165d43..9806380ec671 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -3399,7 +3399,7 @@ bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region) case NL80211_DFS_JP: return true; default: - pr_debug("Ignoring uknown DFS master region: %d\n", dfs_region); + pr_debug("Ignoring unknown DFS master region: %d\n", dfs_region); return false; } } From 39c1134c66b4552f665da576cb625f184a44a8a3 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Mon, 14 May 2018 22:33:34 +0200 Subject: [PATCH 18/30] mac80211: fix TX aggregation stop race The mac80211 tear down code is not waiting for the driver call back. This can bring down the the TX path (TID) till the user manually reconnects. (Observed with iwldvm and enabled TX aggregation.) The race can be prevented when the ampdu_mlme worker handles the tear down. The race: * ieee80211_sta_tear_down_BA_sessions calls ___ieee80211_stop_tx_ba_session for all TIDs, * then cancels the ampdu_mlme worker * and cleanups the TIDs the driver already has called back for. * ieee80211_stop_tx_ba_cb will never be called for a TID if the callback came after the the check in ieee80211_sta_tear_down_BA_sessions. Signed-off-by: Alexander Wetzel [johannes: "enabled" -> "blocked" and invert logic, simplify init] Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index c78036a0ac94..26a7ba3b698f 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -301,26 +301,27 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, ___ieee80211_stop_tx_ba_session(sta, i, reason); mutex_unlock(&sta->ampdu_mlme.mtx); - /* stopping might queue the work again - so cancel only afterwards */ - cancel_work_sync(&sta->ampdu_mlme.work); - /* * In case the tear down is part of a reconfigure due to HW restart * request, it is possible that the low level driver requested to stop * the BA session, so handle it to properly clean tid_tx data. */ - mutex_lock(&sta->ampdu_mlme.mtx); - for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - struct tid_ampdu_tx *tid_tx = - rcu_dereference_protected_tid_tx(sta, i); + if(reason == AGG_STOP_DESTROY_STA) { + cancel_work_sync(&sta->ampdu_mlme.work); - if (!tid_tx) - continue; + mutex_lock(&sta->ampdu_mlme.mtx); + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { + struct tid_ampdu_tx *tid_tx = + rcu_dereference_protected_tid_tx(sta, i); - if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) - ieee80211_stop_tx_ba_cb(sta, i, tid_tx); + if (!tid_tx) + continue; + + if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) + ieee80211_stop_tx_ba_cb(sta, i, tid_tx); + } + mutex_unlock(&sta->ampdu_mlme.mtx); } - mutex_unlock(&sta->ampdu_mlme.mtx); } void ieee80211_ba_session_work(struct work_struct *work) @@ -328,16 +329,11 @@ void ieee80211_ba_session_work(struct work_struct *work) struct sta_info *sta = container_of(work, struct sta_info, ampdu_mlme.work); struct tid_ampdu_tx *tid_tx; + bool blocked; int tid; - /* - * When this flag is set, new sessions should be - * blocked, and existing sessions will be torn - * down by the code that set the flag, so this - * need not run. - */ - if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) - return; + /* When this flag is set, new sessions should be blocked. */ + blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA); mutex_lock(&sta->ampdu_mlme.mtx); for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { @@ -352,7 +348,8 @@ void ieee80211_ba_session_work(struct work_struct *work) sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_UNSPECIFIED, true); - if (test_and_clear_bit(tid, + if (!blocked && + test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl)) ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, IEEE80211_MAX_AMPDU_BUF, @@ -367,7 +364,7 @@ void ieee80211_ba_session_work(struct work_struct *work) spin_lock_bh(&sta->lock); tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; - if (tid_tx) { + if (!blocked && tid_tx) { /* * Assign it over to the normal tid_tx array * where it "goes live". @@ -390,7 +387,8 @@ void ieee80211_ba_session_work(struct work_struct *work) if (!tid_tx) continue; - if (test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) + if (!blocked && + test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) ieee80211_start_tx_ba_cb(sta, tid, tid_tx); if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) ___ieee80211_stop_tx_ba_session(sta, tid, From 0fdf1493b41eb64fc7e8c8e1b8830a4bd8c4bbca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 May 2018 11:40:44 +0200 Subject: [PATCH 19/30] mac80211: allocate and fill tidstats only when needed This fixes memory leaks in the case where we just have the station info on the stack for internal usage without sending it to cfg80211. Fixes: 8689c051a201 ("cfg80211: dynamically allocate per-tid stats for station info") Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 4 ++-- net/mac80211/ethtool.c | 4 ++-- net/mac80211/sta_info.c | 7 ++++--- net/mac80211/sta_info.h | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5ce9d121af2b..bdf6fa78d0d2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -695,7 +695,7 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, if (sta) { ret = 0; memcpy(mac, sta->sta.addr, ETH_ALEN); - sta_set_sinfo(sta, sinfo); + sta_set_sinfo(sta, sinfo, true); } mutex_unlock(&local->sta_mtx); @@ -724,7 +724,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, sta = sta_info_get_bss(sdata, mac); if (sta) { ret = 0; - sta_set_sinfo(sta, sinfo); + sta_set_sinfo(sta, sinfo, true); } mutex_unlock(&local->sta_mtx); diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index 2ba5686cbcab..690c142a7a44 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -108,7 +108,7 @@ static void ieee80211_get_stats(struct net_device *dev, goto do_survey; memset(&sinfo, 0, sizeof(sinfo)); - sta_set_sinfo(sta, &sinfo); + sta_set_sinfo(sta, &sinfo, false); i = 0; ADD_STA_STATS(sta); @@ -135,7 +135,7 @@ static void ieee80211_get_stats(struct net_device *dev, continue; memset(&sinfo, 0, sizeof(sinfo)); - sta_set_sinfo(sta, &sinfo); + sta_set_sinfo(sta, &sinfo, false); i = 0; ADD_STA_STATS(sta); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 04d47689b557..6428f1ac37b6 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1008,7 +1008,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta) sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); if (sinfo) - sta_set_sinfo(sta, sinfo); + sta_set_sinfo(sta, sinfo, true); cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL); kfree(sinfo); @@ -2079,7 +2079,8 @@ static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats) return value; } -void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) +void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, + bool tidstats) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; @@ -2233,7 +2234,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); } - if (!cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) { + if (tidstats && !cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) { for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index d79bd6eeb549..81b35f623792 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -744,7 +744,8 @@ static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); -void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo); +void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, + bool tidstats); u32 sta_get_expected_throughput(struct sta_info *sta); From 7ea3e110f2f8ba23f330c2f702f556acd539bcb8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 May 2018 11:40:44 +0200 Subject: [PATCH 20/30] cfg80211: release station info tidstats where needed This fixes memory leaks in cases where we got the station info but failed sending it out properly. Fixes: 8689c051a201 ("cfg80211: dynamically allocate per-tid stats for station info") Reviewed-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 +++++++++++++ net/wireless/nl80211.c | 11 ++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8984d24d68b7..11a218445448 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5710,6 +5710,19 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, */ int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp); +/** + * cfg80211_sinfo_release_content - release contents of station info + * @sinfo: the station information + * + * Releases any potentially allocated sub-information of the station + * information, but not the struct itself (since it's typically on + * the stack.) + */ +static inline void cfg80211_sinfo_release_content(struct station_info *sinfo) +{ + kfree(sinfo->pertid); +} + /** * cfg80211_new_sta - notify userspace about station * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7daceb1f253d..e4a52a2b5e65 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4702,7 +4702,6 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, } nla_nest_end(msg, tidsattr); - kfree(sinfo->pertid); } nla_nest_end(msg, sinfoattr); @@ -4712,10 +4711,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, sinfo->assoc_req_ies)) goto nla_put_failure; + cfg80211_sinfo_release_content(sinfo); genlmsg_end(msg, hdr); return 0; nla_put_failure: + cfg80211_sinfo_release_content(sinfo); genlmsg_cancel(msg, hdr); return -EMSGSIZE; } @@ -4797,8 +4798,10 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) + if (!msg) { + cfg80211_sinfo_release_content(sinfo); return -ENOMEM; + } if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, info->snd_portid, info->snd_seq, 0, @@ -14624,8 +14627,10 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, trace_cfg80211_del_sta(dev, mac_addr); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) + if (!msg) { + cfg80211_sinfo_release_content(sinfo); return; + } if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, rdev, dev, mac_addr, sinfo) < 0) { From ba8f566a5be07dae46c2e562f4243c687ba6dac9 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 21 May 2018 19:21:42 -0500 Subject: [PATCH 21/30] nl80211: Fix compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 7ea3e110f2f8ba23f330c2f702f556acd539bcb8 seems to have introduced: net/wireless/nl80211.c: In function ‘nl80211_get_station’: net/wireless/nl80211.c:4802:34: error: incompatible type for argument 1 of ‘cfg80211_sinfo_release_content’ cfg80211_sinfo_release_content(sinfo); ^~~~~ In file included from net/wireless/nl80211.c:24:0: ./include/net/cfg80211.h:5721:20: note: expected ‘struct station_info *’ but argument is of type ‘struct station_info’ static inline void cfg80211_sinfo_release_content(struct station_info *sinfo) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fixes: 7ea3e110f2f8 ("cfg80211: release station info tidstats where needed") Signed-off-by: Denis Kenzior Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e4a52a2b5e65..462e028ad452 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4799,7 +4799,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { - cfg80211_sinfo_release_content(sinfo); + cfg80211_sinfo_release_content(&sinfo); return -ENOMEM; } From f3a7ca64587f58686d4e2e894e9abbfbc9dffb25 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 May 2018 11:31:59 +0200 Subject: [PATCH 22/30] cfg80211: add missing kernel-doc Add the kernel-doc missed earlier. Fixes: 52539ca89f36 ("cfg80211: Expose TXQ stats and parameters to userspace") Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 11a218445448..df33c2766d22 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3856,6 +3856,10 @@ struct wiphy_iftype_ext_capab { * bitmap of &enum nl80211_band values. For instance, for * NL80211_BAND_2GHZ, bit 0 would be set * (i.e. BIT(NL80211_BAND_2GHZ)). + * + * @txq_limit: configuration of internal TX queue frame limit + * @txq_memory_limit: configuration internal TX queue memory limit + * @txq_quantum: configuration of internal TX queue scheduler quantum */ struct wiphy { /* assign these fields before you register the wiphy */ From d4e36e5554eb92f3ec7fedad3efb602570584df4 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Fri, 20 Apr 2018 13:49:25 +0300 Subject: [PATCH 23/30] mac80211: Support adding duration for prepare_tx() callback There are specific cases, such as SAE authentication exchange, that might require long duration to complete. For such cases, add support for indicating to the driver the required duration of the prepare_tx() operation, so the driver would still be able to complete the frame exchange. Currently, indicate the duration only for SAE authentication exchange, as SAE authentication can take up to 2000 msec (as defined in IEEE P802.11-REVmd D1.0 p. 3504). As the patch modified the prepare_tx() callback API, also modify the relevant code in iwlwifi. Signed-off-by: Ilan Peer Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/main.c | 3 ++- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 6 ++++- include/net/mac80211.h | 5 +++- net/mac80211/driver-ops.h | 8 +++--- net/mac80211/mlme.c | 17 ++++++++----- net/mac80211/trace.h | 25 ++++++++++++++++--- 6 files changed, 49 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a3be8add56e1..b6663c80e7dd 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2544,7 +2544,8 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, } static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + u16 duration) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 5f0701c992a4..ec91dd90acfd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2844,7 +2844,8 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, } static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + u16 req_duration) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; @@ -2857,6 +2858,9 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX)) return; + if (req_duration > duration) + duration = req_duration; + mutex_lock(&mvm->mutex); /* Try really hard to protect the session and hear a beacon */ iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 604d738a2128..851a5e19ae32 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3378,6 +3378,8 @@ enum ieee80211_reconfig_type { * frame in case that no beacon was heard from the AP/P2P GO. * The callback will be called before each transmission and upon return * mac80211 will transmit the frame right away. + * If duration is greater than zero, mac80211 hints to the driver the + * duration for which the operation is requested. * The callback is optional and can (should!) sleep. * * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending @@ -3697,7 +3699,8 @@ struct ieee80211_ops { u32 sset, u8 *data); void (*mgd_prepare_tx)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); + struct ieee80211_vif *vif, + u16 duration); void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4d82fe7d627c..8f6998091d26 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -2,6 +2,7 @@ /* * Portions of this file * Copyright(c) 2016 Intel Deutschland GmbH +* Copyright (C) 2018 Intel Corporation */ #ifndef __MAC80211_DRIVER_OPS @@ -813,7 +814,8 @@ drv_allow_buffered_frames(struct ieee80211_local *local, } static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) + struct ieee80211_sub_if_data *sdata, + u16 duration) { might_sleep(); @@ -821,9 +823,9 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, return; WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); - trace_drv_mgd_prepare_tx(local, sdata); + trace_drv_mgd_prepare_tx(local, sdata, duration); if (local->ops->mgd_prepare_tx) - local->ops->mgd_prepare_tx(&local->hw, &sdata->vif); + local->ops->mgd_prepare_tx(&local->hw, &sdata->vif, duration); trace_drv_return_void(local); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 233068756502..a59187c016e0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -864,7 +864,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) return; } - drv_mgd_prepare_tx(local, sdata); + drv_mgd_prepare_tx(local, sdata, 0); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) @@ -2022,7 +2022,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, */ if (ieee80211_hw_check(&local->hw, DEAUTH_NEED_MGD_TX_PREP) && !ifmgd->have_beacon) - drv_mgd_prepare_tx(sdata->local, sdata); + drv_mgd_prepare_tx(sdata->local, sdata, 0); ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype, reason, tx, frame_buf); @@ -2560,7 +2560,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, if (!elems.challenge) return; auth_data->expected_transaction = 4; - drv_mgd_prepare_tx(sdata->local, sdata); + drv_mgd_prepare_tx(sdata->local, sdata, 0); if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_INTFL_MLME_CONN_TX; @@ -3769,6 +3769,7 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) u32 tx_flags = 0; u16 trans = 1; u16 status = 0; + u16 prepare_tx_duration = 0; sdata_assert_lock(sdata); @@ -3790,7 +3791,11 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) return -ETIMEDOUT; } - drv_mgd_prepare_tx(local, sdata); + if (auth_data->algorithm == WLAN_AUTH_SAE) + prepare_tx_duration = + jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE); + + drv_mgd_prepare_tx(local, sdata, prepare_tx_duration); sdata_info(sdata, "send auth to %pM (try %d/%d)\n", auth_data->bss->bssid, auth_data->tries, @@ -4994,7 +4999,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code)); - drv_mgd_prepare_tx(sdata->local, sdata); + drv_mgd_prepare_tx(sdata->local, sdata, 0); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, @@ -5014,7 +5019,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code)); - drv_mgd_prepare_tx(sdata->local, sdata); + drv_mgd_prepare_tx(sdata->local, sdata, 0); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 591ad02e1fa4..80a7edf8d314 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2,6 +2,7 @@ /* * Portions of this file * Copyright(c) 2016 Intel Deutschland GmbH +* Copyright (C) 2018 Intel Corporation */ #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) @@ -1413,11 +1414,29 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames, TP_ARGS(local, sta, tids, num_frames, reason, more_data) ); -DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, +TRACE_EVENT(drv_mgd_prepare_tx, TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata), + struct ieee80211_sub_if_data *sdata, + u16 duration), - TP_ARGS(local, sdata) + TP_ARGS(local, sdata, duration), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(u32, duration) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->duration = duration; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " duration: %u", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->duration + ) ); DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover, From d1e23c9456b2881b71732594f4cdb52d78aedecb Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Mon, 21 May 2018 10:31:13 -0500 Subject: [PATCH 24/30] nl80211: Optimize cfg80211_bss_expire invocations Only invoke cfg80211_bss_expire on the first nl80211_dump_scan invocation to avoid (likely) redundant processing. Signed-off-by: Denis Kenzior Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a3dcea2fbd7a..451f12ecb894 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8100,7 +8100,15 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); - cfg80211_bss_expire(rdev); + + /* + * dump_scan will be called multiple times to break up the scan results + * into multiple messages. It is unlikely that any more bss-es will be + * expired after the first call, so only call only call this on the + * first dump_scan invocation. + */ + if (start == 0) + cfg80211_bss_expire(rdev); cb->seq = rdev->bss_generation; From 76804d28c32ec14e1fdb3981623e5b7a4bc1c739 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Tue, 22 May 2018 10:19:06 +0200 Subject: [PATCH 25/30] cfg80211: use separate struct for FILS parameters Put FILS related parameters into their own struct definition so it can be reused for roam events in subsequent change. Reviewed-by: Jithu Jance Reviewed-by: Eylon Pedinovsky Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 44 +++++++++++++++++++++++++----------------- net/wireless/nl80211.c | 22 ++++++++++----------- net/wireless/sme.c | 40 ++++++++++++++++++++------------------ 3 files changed, 58 insertions(+), 48 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index df33c2766d22..0e4a2a04d55d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5420,6 +5420,30 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) #define CFG80211_TESTMODE_DUMP(cmd) #endif +/** + * struct cfg80211_fils_resp_params - FILS connection response params + * @kek: KEK derived from a successful FILS connection (may be %NULL) + * @kek_len: Length of @fils_kek in octets + * @update_erp_next_seq_num: Boolean value to specify whether the value in + * @erp_next_seq_num is valid. + * @erp_next_seq_num: The next sequence number to use in ERP message in + * FILS Authentication. This value should be specified irrespective of the + * status for a FILS connection. + * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL). + * @pmk_len: Length of @pmk in octets + * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID + * used for this FILS connection (may be %NULL). + */ +struct cfg80211_fils_resp_params { + const u8 *kek; + size_t kek_len; + bool update_erp_next_seq_num; + u16 erp_next_seq_num; + const u8 *pmk; + size_t pmk_len; + const u8 *pmkid; +}; + /** * struct cfg80211_connect_resp_params - Connection response params * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use @@ -5438,17 +5462,7 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * @req_ie_len: Association request IEs length * @resp_ie: Association response IEs (may be %NULL) * @resp_ie_len: Association response IEs length - * @fils_kek: KEK derived from a successful FILS connection (may be %NULL) - * @fils_kek_len: Length of @fils_kek in octets - * @update_erp_next_seq_num: Boolean value to specify whether the value in - * @fils_erp_next_seq_num is valid. - * @fils_erp_next_seq_num: The next sequence number to use in ERP message in - * FILS Authentication. This value should be specified irrespective of the - * status for a FILS connection. - * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL). - * @pmk_len: Length of @pmk in octets - * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID - * used for this FILS connection (may be %NULL). + * @fils: FILS connection response parameters. * @timeout_reason: Reason for connection timeout. This is used when the * connection fails due to a timeout instead of an explicit rejection from * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is @@ -5464,13 +5478,7 @@ struct cfg80211_connect_resp_params { size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; - const u8 *fils_kek; - size_t fils_kek_len; - bool update_erp_next_seq_num; - u16 fils_erp_next_seq_num; - const u8 *pmk; - size_t pmk_len; - const u8 *pmkid; + struct cfg80211_fils_resp_params fils; enum nl80211_timeout_reason timeout_reason; }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 451f12ecb894..3ab443b13bb0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14206,8 +14206,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, void *hdr; msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + - cr->fils_kek_len + cr->pmk_len + - (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp); + cr->fils.kek_len + cr->fils.pmk_len + + (cr->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!msg) return; @@ -14233,17 +14233,17 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, (cr->resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len, cr->resp_ie)) || - (cr->update_erp_next_seq_num && + (cr->fils.update_erp_next_seq_num && nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, - cr->fils_erp_next_seq_num)) || + cr->fils.erp_next_seq_num)) || (cr->status == WLAN_STATUS_SUCCESS && - ((cr->fils_kek && - nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len, - cr->fils_kek)) || - (cr->pmk && - nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) || - (cr->pmkid && - nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid))))) + ((cr->fils.kek && + nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils.kek_len, + cr->fils.kek)) || + (cr->fils.pmk && + nla_put(msg, NL80211_ATTR_PMK, cr->fils.pmk_len, cr->fils.pmk)) || + (cr->fils.pmkid && + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid))))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 5df6b33db786..73881fb7f86d 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -803,8 +803,8 @@ void cfg80211_connect_done(struct net_device *dev, ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + params->req_ie_len + params->resp_ie_len + - params->fils_kek_len + params->pmk_len + - (params->pmkid ? WLAN_PMKID_LEN : 0), gfp); + params->fils.kek_len + params->fils.pmk_len + + (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!ev) { cfg80211_put_bss(wdev->wiphy, params->bss); return; @@ -831,27 +831,29 @@ void cfg80211_connect_done(struct net_device *dev, params->resp_ie_len); next += params->resp_ie_len; } - if (params->fils_kek_len) { - ev->cr.fils_kek = next; - ev->cr.fils_kek_len = params->fils_kek_len; - memcpy((void *)ev->cr.fils_kek, params->fils_kek, - params->fils_kek_len); - next += params->fils_kek_len; + if (params->fils.kek_len) { + ev->cr.fils.kek = next; + ev->cr.fils.kek_len = params->fils.kek_len; + memcpy((void *)ev->cr.fils.kek, params->fils.kek, + params->fils.kek_len); + next += params->fils.kek_len; } - if (params->pmk_len) { - ev->cr.pmk = next; - ev->cr.pmk_len = params->pmk_len; - memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len); - next += params->pmk_len; + if (params->fils.pmk_len) { + ev->cr.fils.pmk = next; + ev->cr.fils.pmk_len = params->fils.pmk_len; + memcpy((void *)ev->cr.fils.pmk, params->fils.pmk, + params->fils.pmk_len); + next += params->fils.pmk_len; } - if (params->pmkid) { - ev->cr.pmkid = next; - memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN); + if (params->fils.pmkid) { + ev->cr.fils.pmkid = next; + memcpy((void *)ev->cr.fils.pmkid, params->fils.pmkid, + WLAN_PMKID_LEN); next += WLAN_PMKID_LEN; } - ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num; - if (params->update_erp_next_seq_num) - ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num; + ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num; + if (params->fils.update_erp_next_seq_num) + ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num; if (params->bss) cfg80211_hold_bss(bss_from_pub(params->bss)); ev->cr.bss = params->bss; From e841b7b11ebd0b359e07bb3d7caf15dca1a80b72 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Tue, 22 May 2018 10:19:07 +0200 Subject: [PATCH 26/30] nl80211: add FILS related parameters to ROAM event In case of FILS shared key offload the parameters can change upon roaming of which user-space needs to be notified. Reviewed-by: Jithu Jance Reviewed-by: Eylon Pedinovsky Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 3 ++- net/wireless/nl80211.c | 16 ++++++++++-- net/wireless/sme.c | 48 ++++++++++++++++++++++++++++++------ 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0e4a2a04d55d..1e7c0df4fe33 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5624,6 +5624,7 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length + * @fils: FILS related roaming information. */ struct cfg80211_roam_info { struct ieee80211_channel *channel; @@ -5633,6 +5634,7 @@ struct cfg80211_roam_info { size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; + struct cfg80211_fils_resp_params fils; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 83ed1dd757e6..0a412335d56b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -215,7 +215,8 @@ * as specified in IETF RFC 6696. * * When FILS shared key authentication is completed, driver needs to provide the - * below additional parameters to userspace. + * below additional parameters to userspace, which can be either after setting + * up a connection or after roaming. * %NL80211_ATTR_FILS_KEK - used for key renewal * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3ab443b13bb0..ae57f9712d7d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14264,7 +14264,9 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, void *hdr; const u8 *bssid = info->bss ? info->bss->bssid : info->bssid; - msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len, gfp); + msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len + + info->fils.kek_len + info->fils.pmk_len + + (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!msg) return; @@ -14282,7 +14284,17 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, info->req_ie)) || (info->resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, info->resp_ie_len, - info->resp_ie))) + info->resp_ie)) || + (info->fils.update_erp_next_seq_num && + nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + info->fils.erp_next_seq_num)) || + (info->fils.kek && + nla_put(msg, NL80211_ATTR_FILS_KEK, info->fils.kek_len, + info->fils.kek)) || + (info->fils.pmk && + nla_put(msg, NL80211_ATTR_PMK, info->fils.pmk_len, info->fils.pmk)) || + (info->fils.pmkid && + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 73881fb7f86d..d536b07582f8 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -932,6 +932,7 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; + u8 *next; if (!info->bss) { info->bss = cfg80211_get_bss(wdev->wiphy, info->channel, @@ -944,19 +945,52 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, if (WARN_ON(!info->bss)) return; - ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len, gfp); + ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len + + info->fils.kek_len + info->fils.pmk_len + + (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!ev) { cfg80211_put_bss(wdev->wiphy, info->bss); return; } ev->type = EVENT_ROAMED; - ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->rm.req_ie_len = info->req_ie_len; - memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len); - ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + info->req_ie_len; - ev->rm.resp_ie_len = info->resp_ie_len; - memcpy((void *)ev->rm.resp_ie, info->resp_ie, info->resp_ie_len); + next = ((u8 *)ev) + sizeof(*ev); + if (info->req_ie_len) { + ev->rm.req_ie = next; + ev->rm.req_ie_len = info->req_ie_len; + memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len); + next += info->req_ie_len; + } + if (info->resp_ie_len) { + ev->rm.resp_ie = next; + ev->rm.resp_ie_len = info->resp_ie_len; + memcpy((void *)ev->rm.resp_ie, info->resp_ie, + info->resp_ie_len); + next += info->resp_ie_len; + } + if (info->fils.kek_len) { + ev->rm.fils.kek = next; + ev->rm.fils.kek_len = info->fils.kek_len; + memcpy((void *)ev->rm.fils.kek, info->fils.kek, + info->fils.kek_len); + next += info->fils.kek_len; + } + if (info->fils.pmk_len) { + ev->rm.fils.pmk = next; + ev->rm.fils.pmk_len = info->fils.pmk_len; + memcpy((void *)ev->rm.fils.pmk, info->fils.pmk, + info->fils.pmk_len); + next += info->fils.pmk_len; + } + if (info->fils.pmkid) { + ev->rm.fils.pmkid = next; + memcpy((void *)ev->rm.fils.pmkid, info->fils.pmkid, + WLAN_PMKID_LEN); + next += WLAN_PMKID_LEN; + } + ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num; + if (info->fils.update_erp_next_seq_num) + ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; ev->rm.bss = info->bss; spin_lock_irqsave(&wdev->event_lock, flags); From 7f9a3e150ec7d3596386449c15aefb59904a1266 Mon Sep 17 00:00:00 2001 From: Vidyullatha Kanchanapally Date: Tue, 22 May 2018 10:19:08 +0200 Subject: [PATCH 27/30] nl80211: Update ERP info using NL80211_CMD_UPDATE_CONNECT_PARAMS Use NL80211_CMD_UPDATE_CONNECT_PARAMS to update new ERP information, Association IEs and the Authentication type to driver / firmware which will be used in subsequent roamings. Signed-off-by: Vidyullatha Kanchanapally [arend: extended fils-sk kernel doc and added check in wiphy_register()] Reviewed-by: Jithu Jance Reviewed-by: Eylon Pedinovsky Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ++++ include/uapi/linux/nl80211.h | 3 ++- net/wireless/core.c | 4 +++ net/wireless/nl80211.c | 52 ++++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1e7c0df4fe33..5fbfe61f41c6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2225,9 +2225,14 @@ struct cfg80211_connect_params { * have to be updated as part of update_connect_params() call. * * @UPDATE_ASSOC_IES: Indicates whether association request IEs are updated + * @UPDATE_FILS_ERP_INFO: Indicates that FILS connection parameters (realm, + * username, erp sequence number and rrk) are updated + * @UPDATE_AUTH_TYPE: Indicates that authentication type is updated */ enum cfg80211_connect_params_changed { UPDATE_ASSOC_IES = BIT(0), + UPDATE_FILS_ERP_INFO = BIT(1), + UPDATE_AUTH_TYPE = BIT(2), }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0a412335d56b..06f9af23156b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -204,7 +204,8 @@ * FILS shared key authentication offload should be able to construct the * authentication and association frames for FILS shared key authentication and * eventually do a key derivation as per IEEE 802.11ai. The below additional - * parameters should be given to driver in %NL80211_CMD_CONNECT. + * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in + * %NL80211_CMD_UPDATE_CONNECT_PARAMS. * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message diff --git a/net/wireless/core.c b/net/wireless/core.c index c0fd8a85e7f7..5fe35aafdd9c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -725,6 +725,10 @@ int wiphy_register(struct wiphy *wiphy) (!rdev->ops->set_pmk || !rdev->ops->del_pmk))) return -EINVAL; + if (WARN_ON(!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && + rdev->ops->update_connect_params)) + return -EINVAL; + if (wiphy->addresses) memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ae57f9712d7d..bdf73b24cc09 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9429,6 +9429,8 @@ static int nl80211_update_connect_params(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; + bool fils_sk_offload; + u32 auth_type; u32 changed = 0; int ret; @@ -9443,6 +9445,56 @@ static int nl80211_update_connect_params(struct sk_buff *skb, changed |= UPDATE_ASSOC_IES; } + fils_sk_offload = wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD); + + /* + * when driver supports fils-sk offload all attributes must be + * provided. So the else covers "fils-sk-not-all" and + * "no-fils-sk-any". + */ + if (fils_sk_offload && + info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && + info->attrs[NL80211_ATTR_FILS_ERP_REALM] && + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + connect.fils_erp_username = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_username_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_realm = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_realm_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_next_seq_num = + nla_get_u16( + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); + connect.fils_erp_rrk = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + connect.fils_erp_rrk_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + changed |= UPDATE_FILS_ERP_INFO; + } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || + info->attrs[NL80211_ATTR_FILS_ERP_REALM] || + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(rdev, auth_type, + NL80211_CMD_CONNECT)) + return -EINVAL; + + if (auth_type == NL80211_AUTHTYPE_FILS_SK && + fils_sk_offload && !(changed & UPDATE_FILS_ERP_INFO)) + return -EINVAL; + + connect.auth_type = auth_type; + changed |= UPDATE_AUTH_TYPE; + } + wdev_lock(dev->ieee80211_ptr); if (!wdev->current_bss) ret = -ENOLINK; From d874cd74306694433b5b2c5ce32ddba48fbc360e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 May 2018 14:29:31 -0700 Subject: [PATCH 28/30] rfkill: Rename rfkill_any_led_trigger* functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename these functions to rfkill_global_led_trigger*, as they are going to be extended to handle another global rfkill led trigger. This commit does not change any functionality. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 59d0eb960275..6d64d14f4b0a 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -178,9 +178,9 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill) } static struct led_trigger rfkill_any_led_trigger; -static struct work_struct rfkill_any_work; +static struct work_struct rfkill_global_led_trigger_work; -static void rfkill_any_led_trigger_worker(struct work_struct *work) +static void rfkill_global_led_trigger_worker(struct work_struct *work) { enum led_brightness brightness = LED_OFF; struct rfkill *rfkill; @@ -197,28 +197,29 @@ static void rfkill_any_led_trigger_worker(struct work_struct *work) led_trigger_event(&rfkill_any_led_trigger, brightness); } -static void rfkill_any_led_trigger_event(void) +static void rfkill_global_led_trigger_event(void) { - schedule_work(&rfkill_any_work); + schedule_work(&rfkill_global_led_trigger_work); } -static void rfkill_any_led_trigger_activate(struct led_classdev *led_cdev) +static void rfkill_global_led_trigger_activate(struct led_classdev *led_cdev) { - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); } -static int rfkill_any_led_trigger_register(void) +static int rfkill_global_led_trigger_register(void) { - INIT_WORK(&rfkill_any_work, rfkill_any_led_trigger_worker); + INIT_WORK(&rfkill_global_led_trigger_work, + rfkill_global_led_trigger_worker); rfkill_any_led_trigger.name = "rfkill-any"; - rfkill_any_led_trigger.activate = rfkill_any_led_trigger_activate; + rfkill_any_led_trigger.activate = rfkill_global_led_trigger_activate; return led_trigger_register(&rfkill_any_led_trigger); } -static void rfkill_any_led_trigger_unregister(void) +static void rfkill_global_led_trigger_unregister(void) { led_trigger_unregister(&rfkill_any_led_trigger); - cancel_work_sync(&rfkill_any_work); + cancel_work_sync(&rfkill_global_led_trigger_work); } #else static void rfkill_led_trigger_event(struct rfkill *rfkill) @@ -234,16 +235,16 @@ static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill) { } -static void rfkill_any_led_trigger_event(void) +static void rfkill_global_led_trigger_event(void) { } -static int rfkill_any_led_trigger_register(void) +static int rfkill_global_led_trigger_register(void) { return 0; } -static void rfkill_any_led_trigger_unregister(void) +static void rfkill_global_led_trigger_unregister(void) { } #endif /* CONFIG_RFKILL_LEDS */ @@ -354,7 +355,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked) spin_unlock_irqrestore(&rfkill->lock, flags); rfkill_led_trigger_event(rfkill); - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); if (prev != curr) rfkill_event(rfkill); @@ -535,7 +536,7 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) spin_unlock_irqrestore(&rfkill->lock, flags); rfkill_led_trigger_event(rfkill); - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); if (rfkill->registered && prev != blocked) schedule_work(&rfkill->uevent_work); @@ -579,7 +580,7 @@ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) schedule_work(&rfkill->uevent_work); rfkill_led_trigger_event(rfkill); - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); return blocked; } @@ -629,7 +630,7 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) schedule_work(&rfkill->uevent_work); rfkill_led_trigger_event(rfkill); - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); } } EXPORT_SYMBOL(rfkill_set_states); @@ -1046,7 +1047,7 @@ int __must_check rfkill_register(struct rfkill *rfkill) #endif } - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); rfkill_send_events(rfkill, RFKILL_OP_ADD); mutex_unlock(&rfkill_global_mutex); @@ -1079,7 +1080,7 @@ void rfkill_unregister(struct rfkill *rfkill) mutex_lock(&rfkill_global_mutex); rfkill_send_events(rfkill, RFKILL_OP_DEL); list_del_init(&rfkill->node); - rfkill_any_led_trigger_event(); + rfkill_global_led_trigger_event(); mutex_unlock(&rfkill_global_mutex); rfkill_led_trigger_unregister(rfkill); @@ -1332,7 +1333,7 @@ static int __init rfkill_init(void) if (error) goto error_misc; - error = rfkill_any_led_trigger_register(); + error = rfkill_global_led_trigger_register(); if (error) goto error_led_trigger; @@ -1346,7 +1347,7 @@ static int __init rfkill_init(void) #ifdef CONFIG_RFKILL_INPUT error_input: - rfkill_any_led_trigger_unregister(); + rfkill_global_led_trigger_unregister(); #endif error_led_trigger: misc_deregister(&rfkill_miscdev); @@ -1362,7 +1363,7 @@ static void __exit rfkill_exit(void) #ifdef CONFIG_RFKILL_INPUT rfkill_handler_exit(); #endif - rfkill_any_led_trigger_unregister(); + rfkill_global_led_trigger_unregister(); misc_deregister(&rfkill_miscdev); class_unregister(&rfkill_class); } From 232aa23ec354029a58280e79d815ee4746c0bbb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Rechi=20Vita?= Date: Tue, 22 May 2018 14:29:32 -0700 Subject: [PATCH 29/30] rfkill: Create rfkill-none LED trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates a new trigger rfkill-none, as a complement to rfkill-any, which drives LEDs when any radio is enabled. The new trigger is meant to turn a LED ON whenever all radios are OFF, and turn it OFF otherwise. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 6d64d14f4b0a..a7a4e6ff9be2 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -178,6 +178,7 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill) } static struct led_trigger rfkill_any_led_trigger; +static struct led_trigger rfkill_none_led_trigger; static struct work_struct rfkill_global_led_trigger_work; static void rfkill_global_led_trigger_worker(struct work_struct *work) @@ -195,6 +196,8 @@ static void rfkill_global_led_trigger_worker(struct work_struct *work) mutex_unlock(&rfkill_global_mutex); led_trigger_event(&rfkill_any_led_trigger, brightness); + led_trigger_event(&rfkill_none_led_trigger, + brightness == LED_OFF ? LED_FULL : LED_OFF); } static void rfkill_global_led_trigger_event(void) @@ -202,22 +205,32 @@ static void rfkill_global_led_trigger_event(void) schedule_work(&rfkill_global_led_trigger_work); } -static void rfkill_global_led_trigger_activate(struct led_classdev *led_cdev) -{ - rfkill_global_led_trigger_event(); -} - static int rfkill_global_led_trigger_register(void) { + int ret; + INIT_WORK(&rfkill_global_led_trigger_work, rfkill_global_led_trigger_worker); + rfkill_any_led_trigger.name = "rfkill-any"; - rfkill_any_led_trigger.activate = rfkill_global_led_trigger_activate; - return led_trigger_register(&rfkill_any_led_trigger); + ret = led_trigger_register(&rfkill_any_led_trigger); + if (ret) + return ret; + + rfkill_none_led_trigger.name = "rfkill-none"; + ret = led_trigger_register(&rfkill_none_led_trigger); + if (ret) + led_trigger_unregister(&rfkill_any_led_trigger); + else + /* Delay activation until all global triggers are registered */ + rfkill_global_led_trigger_event(); + + return ret; } static void rfkill_global_led_trigger_unregister(void) { + led_trigger_unregister(&rfkill_none_led_trigger); led_trigger_unregister(&rfkill_any_led_trigger); cancel_work_sync(&rfkill_global_led_trigger_work); } From bad2929733635f80f99930b252757c70372356fe Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Tue, 22 May 2018 02:46:02 +0200 Subject: [PATCH 30/30] nl80211: Reject disconnect commands except from conn_owner Reject NL80211_CMD_DISCONNECT, NL80211_CMD_DISASSOCIATE, NL80211_CMD_DEAUTHENTICATE and NL80211_CMD_ASSOCIATE commands from clients other than the connection owner set in the connect, authenticate or associate commands, if it was set. The main point of this check is to prevent chaos when two processes try to use nl80211 at the same time, it's not a security measure. The same thing should possibly be done for JOIN_IBSS/LEAVE_IBSS and START_AP/STOP_AP. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bdf73b24cc09..bc40a783cb27 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8514,6 +8514,10 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) const u8 *bssid, *ssid; int err, ssid_len = 0; + if (dev->ieee80211_ptr->conn_owner_nlportid && + dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) + return -EPERM; + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -8636,6 +8640,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) u16 reason_code; bool local_state_change; + if (dev->ieee80211_ptr->conn_owner_nlportid && + dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) + return -EPERM; + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -8683,6 +8691,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) u16 reason_code; bool local_state_change; + if (dev->ieee80211_ptr->conn_owner_nlportid && + dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) + return -EPERM; + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -9512,6 +9524,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) u16 reason; int ret; + if (dev->ieee80211_ptr->conn_owner_nlportid && + dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) + return -EPERM; + if (!info->attrs[NL80211_ATTR_REASON_CODE]) reason = WLAN_REASON_DEAUTH_LEAVING; else