From c799ba6eab7a1bf02792e2ced933be3ae5f8daa2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Dec 2015 15:31:10 +0100 Subject: cfg80211: remove CFG80211_REG_DEBUG Instead of having this Kconfig option, which just *floods* the kernel log, * remove the per-channel prints that are fairly useless anyway * convert the conditional printing to pr_debug() Signed-off-by: Johannes Berg --- net/wireless/Kconfig | 13 ------ net/wireless/reg.c | 122 ++++++++++++++------------------------------------- 2 files changed, 34 insertions(+), 101 deletions(-) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index da72ed32f143..ec3bf30dd526 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -61,19 +61,6 @@ config CFG80211_DEVELOPER_WARNINGS on it (or mac80211). -config CFG80211_REG_DEBUG - bool "cfg80211 regulatory debugging" - depends on CFG80211 - default n - ---help--- - You can enable this if you want to debug regulatory changes. - For more information on cfg80211 regulatory refer to the wireless - wiki: - - http://wireless.kernel.org/en/developers/Regulatory - - If unsure, say N. - config CFG80211_CERTIFICATION_ONUS bool "cfg80211 certification onus" depends on CFG80211 && EXPERT diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3b0ce1c484a3..bc76b281ed3a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -60,13 +60,6 @@ #include "regdb.h" #include "nl80211.h" -#ifdef CONFIG_CFG80211_REG_DEBUG -#define REG_DBG_PRINT(format, args...) \ - printk(KERN_DEBUG pr_fmt(format), ##args) -#else -#define REG_DBG_PRINT(args...) -#endif - /* * Grace period we give before making sure all current interfaces reside on * channels allowed by the current regulatory domain. @@ -178,12 +171,10 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy) if (wiphy_regd->dfs_region == regd->dfs_region) goto out; - REG_DBG_PRINT("%s: device specific dfs_region " - "(%s) disagrees with cfg80211's " - "central dfs_region (%s)\n", - dev_name(&wiphy->dev), - reg_dfs_region_str(wiphy_regd->dfs_region), - reg_dfs_region_str(regd->dfs_region)); + pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s)\n", + dev_name(&wiphy->dev), + reg_dfs_region_str(wiphy_regd->dfs_region), + reg_dfs_region_str(regd->dfs_region)); out: return regd->dfs_region; @@ -541,7 +532,7 @@ static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work); static void crda_timeout_work(struct work_struct *work) { - REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); + pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); rtnl_lock(); reg_crda_timeouts++; restore_regulatory_settings(true); @@ -583,7 +574,7 @@ static int call_crda(const char *alpha2) if (!is_world_regdom((char *) alpha2)) pr_debug("Calling CRDA for country: %c%c\n", - alpha2[0], alpha2[1]); + alpha2[0], alpha2[1]); else pr_debug("Calling CRDA to update world regulatory domain\n"); @@ -1130,42 +1121,6 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator) } EXPORT_SYMBOL(reg_initiator_name); -static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd, - struct ieee80211_channel *chan, - const struct ieee80211_reg_rule *reg_rule) -{ -#ifdef CONFIG_CFG80211_REG_DEBUG - const struct ieee80211_power_rule *power_rule; - const struct ieee80211_freq_range *freq_range; - char max_antenna_gain[32], bw[32]; - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (!power_rule->max_antenna_gain) - snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A"); - else - snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d mBi", - power_rule->max_antenna_gain); - - if (reg_rule->flags & NL80211_RRF_AUTO_BW) - snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO", - freq_range->max_bandwidth_khz, - reg_get_max_bandwidth(regd, reg_rule)); - else - snprintf(bw, sizeof(bw), "%d KHz", - freq_range->max_bandwidth_khz); - - REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n", - chan->center_freq); - - REG_DBG_PRINT("(%d KHz - %d KHz @ %s), (%s, %d mBm)\n", - freq_range->start_freq_khz, freq_range->end_freq_khz, - bw, max_antenna_gain, - power_rule->max_eirp); -#endif -} - static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd, const struct ieee80211_reg_rule *reg_rule, const struct ieee80211_channel *chan) @@ -1240,20 +1195,19 @@ static void handle_channel(struct wiphy *wiphy, if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { - REG_DBG_PRINT("Disabling freq %d MHz for good\n", - chan->center_freq); + pr_debug("Disabling freq %d MHz for good\n", + chan->center_freq); chan->orig_flags |= IEEE80211_CHAN_DISABLED; chan->flags = chan->orig_flags; } else { - REG_DBG_PRINT("Disabling freq %d MHz\n", - chan->center_freq); + pr_debug("Disabling freq %d MHz\n", + chan->center_freq); chan->flags |= IEEE80211_CHAN_DISABLED; } return; } regd = reg_get_regdomain(wiphy); - chan_reg_rule_print_dbg(regd, chan, reg_rule); power_rule = ®_rule->power_rule; bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan); @@ -1391,18 +1345,15 @@ static bool ignore_reg_update(struct wiphy *wiphy, return true; if (!lr) { - REG_DBG_PRINT("Ignoring regulatory request set by %s " - "since last_request is not set\n", - reg_initiator_name(initiator)); + pr_debug("Ignoring regulatory request set by %s since last_request is not set\n", + reg_initiator_name(initiator)); return true; } if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { - REG_DBG_PRINT("Ignoring regulatory request set by %s " - "since the driver uses its own custom " - "regulatory domain\n", - reg_initiator_name(initiator)); + pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain\n", + reg_initiator_name(initiator)); return true; } @@ -1413,10 +1364,8 @@ static bool ignore_reg_update(struct wiphy *wiphy, if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(lr->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request set by %s " - "since the driver requires its own regulatory " - "domain to be set first\n", - reg_initiator_name(initiator)); + pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first\n", + reg_initiator_name(initiator)); return true; } @@ -1697,7 +1646,7 @@ static void reg_check_chans_work(struct work_struct *work) { struct cfg80211_registered_device *rdev; - REG_DBG_PRINT("Verifying active interfaces after reg change\n"); + pr_debug("Verifying active interfaces after reg change\n"); rtnl_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) @@ -1779,8 +1728,8 @@ static void handle_channel_custom(struct wiphy *wiphy, } if (IS_ERR(reg_rule)) { - REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", - chan->center_freq); + pr_debug("Disabling freq %d MHz as custom regd has no rule that fits it\n", + chan->center_freq); if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { chan->flags |= IEEE80211_CHAN_DISABLED; } else { @@ -1790,8 +1739,6 @@ static void handle_channel_custom(struct wiphy *wiphy, return; } - chan_reg_rule_print_dbg(regd, chan, reg_rule); - power_rule = ®_rule->power_rule; bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan); @@ -2522,7 +2469,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) if (is_user_regdom_saved()) { /* Unless we're asked to ignore it and reset it */ if (reset_user) { - REG_DBG_PRINT("Restoring regulatory settings including user preference\n"); + pr_debug("Restoring regulatory settings including user preference\n"); user_alpha2[0] = '9'; user_alpha2[1] = '7'; @@ -2532,24 +2479,24 @@ static void restore_alpha2(char *alpha2, bool reset_user) * back as they were for a full restore. */ if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], ieee80211_regdom[1]); + pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } } else { - REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n", - user_alpha2[0], user_alpha2[1]); + pr_debug("Restoring regulatory settings while preserving user preference for: %c%c\n", + user_alpha2[0], user_alpha2[1]); alpha2[0] = user_alpha2[0]; alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], ieee80211_regdom[1]); + pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } else - REG_DBG_PRINT("Restoring regulatory settings\n"); + pr_debug("Restoring regulatory settings\n"); } static void restore_custom_reg_settings(struct wiphy *wiphy) @@ -2661,14 +2608,14 @@ static void restore_regulatory_settings(bool reset_user) list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); spin_unlock(®_requests_lock); - REG_DBG_PRINT("Kicking the queue\n"); + pr_debug("Kicking the queue\n"); schedule_work(®_work); } void regulatory_hint_disconnect(void) { - REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n"); + pr_debug("All devices are disconnected, going to restore regulatory settings\n"); restore_regulatory_settings(false); } @@ -2716,10 +2663,10 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, if (!reg_beacon) return -ENOMEM; - REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n", - beacon_chan->center_freq, - ieee80211_frequency_to_channel(beacon_chan->center_freq), - wiphy_name(wiphy)); + pr_debug("Found new beacon on frequency: %d MHz (Ch %d) on %s\n", + beacon_chan->center_freq, + ieee80211_frequency_to_channel(beacon_chan->center_freq), + wiphy_name(wiphy)); memcpy(®_beacon->chan, beacon_chan, sizeof(struct ieee80211_channel)); @@ -2798,8 +2745,7 @@ bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region) case NL80211_DFS_JP: return true; default: - REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n", - dfs_region); + pr_debug("Ignoring uknown DFS master region: %d\n", dfs_region); return false; } } -- cgit From 23a1f8d44c0bca48f04fc2a2f1edafd826ce6133 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 8 Dec 2015 16:04:31 +0200 Subject: mac80211: process and save VHT MU-MIMO group frame The Group ID Management frame is an Action frame of category VHT. It is transmitted by the AP to assign or change the user position of a STA for one or more group IDs. Process and save the group membership data. Notify underlying driver of changes. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 7 +++++++ include/net/mac80211.h | 17 +++++++++++++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 10 ++++++++++ net/mac80211/mlme.c | 7 +++++++ net/mac80211/rx.c | 5 +++++ net/mac80211/util.c | 3 +++ net/mac80211/vht.c | 25 +++++++++++++++++++++++++ 8 files changed, 76 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 452c0b0d2f32..d9ddb89533a7 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -843,6 +843,8 @@ enum ieee80211_vht_opmode_bits { }; #define WLAN_SA_QUERY_TR_ID_LEN 2 +#define WLAN_MEMBERSHIP_LEN 8 +#define WLAN_USER_POSITION_LEN 16 /** * struct ieee80211_tpc_report_ie @@ -989,6 +991,11 @@ struct ieee80211_mgmt { u8 action_code; u8 operating_mode; } __packed vht_opmode_notif; + struct { + u8 action_code; + u8 membership[WLAN_MEMBERSHIP_LEN]; + u8 position[WLAN_USER_POSITION_LEN]; + } __packed vht_group_notif; struct { u8 action_code; u8 dialog_token; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7c30faff245f..8da483b2c067 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -298,6 +298,7 @@ struct ieee80211_vif_chanctx_switch { * note that this is only called when it changes after the channel * context had been assigned. * @BSS_CHANGED_OCB: OCB join status changed + * @BSS_CHANGED_MU_GROUPS: VHT MU-MIMO group id or user position changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -323,6 +324,7 @@ enum ieee80211_bss_change { BSS_CHANGED_BEACON_INFO = 1<<20, BSS_CHANGED_BANDWIDTH = 1<<21, BSS_CHANGED_OCB = 1<<22, + BSS_CHANGED_MU_GROUPS = 1<<23, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -435,6 +437,19 @@ struct ieee80211_event { } u; }; +/** + * struct ieee80211_mu_group_data - STA's VHT MU-MIMO group data + * + * This structure describes the group id data of VHT MU-MIMO + * + * @membership: 64 bits array - a bit is set if station is member of the group + * @position: 2 bits per group id indicating the position in the group + */ +struct ieee80211_mu_group_data { + u8 membership[WLAN_MEMBERSHIP_LEN]; + u8 position[WLAN_USER_POSITION_LEN]; +}; + /** * struct ieee80211_bss_conf - holds the BSS's changing parameters * @@ -477,6 +492,7 @@ struct ieee80211_event { * @enable_beacon: whether beaconing should be enabled or not * @chandef: Channel definition for this BSS -- the hardware might be * configured a higher bandwidth than this BSS uses, for example. + * @mu_group: VHT MU-MIMO group membership data * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. * This field is only valid when the channel is a wide HT/VHT channel. * Note that with TDLS this can be the case (channel is HT, protection must @@ -535,6 +551,7 @@ struct ieee80211_bss_conf { s32 cqm_rssi_thold; u32 cqm_rssi_hyst; struct cfg80211_chan_def chandef; + struct ieee80211_mu_group_data mu_group; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; int arp_addr_cnt; bool qos; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b84f6aa32c08..747402d8c7a9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1714,6 +1714,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta); enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); void ieee80211_sta_set_rx_nss(struct sta_info *sta); +void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt); u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, u8 opmode, enum ieee80211_band band); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index c9e325d2e120..33ae3c81bfc5 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1271,6 +1271,16 @@ static void ieee80211_iface_work(struct work_struct *work) } } mutex_unlock(&local->sta_mtx); + } else if (ieee80211_is_action(mgmt->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_VHT) { + switch (mgmt->u.action.u.vht_group_notif.action_code) { + case WLAN_VHT_ACTION_GROUPID_MGMT: + ieee80211_process_mu_groups(sdata, mgmt); + break; + default: + WARN_ON(1); + break; + } } else if (ieee80211_is_data_qos(mgmt->frame_control)) { struct ieee80211_hdr *hdr = (void *)mgmt; /* diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1c342e2592c4..31d5881b31fa 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2079,6 +2079,13 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask)); + + /* reset MU-MIMO ownership and group data */ + memset(sdata->vif.bss_conf.mu_group.membership, 0, + sizeof(sdata->vif.bss_conf.mu_group.membership)); + memset(sdata->vif.bss_conf.mu_group.position, 0, + sizeof(sdata->vif.bss_conf.mu_group.position)); + changed |= BSS_CHANGED_MU_GROUPS; sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER; sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bc081850ac0e..a5668b54015f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2738,6 +2738,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) opmode, status->band); goto handled; } + case WLAN_VHT_ACTION_GROUPID_MGMT: { + if (len < IEEE80211_MIN_ACTION_SIZE + 25) + goto invalid; + goto queue; + } default: break; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3943d4bf289c..f4b2c04e7d81 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1928,6 +1928,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_IDLE | BSS_CHANGED_TXPOWER; + if (sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER) + changed |= BSS_CHANGED_MU_GROUPS; + switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: changed |= BSS_CHANGED_ASSOC | diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index c38b2f07a919..050de08bf82e 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -1,6 +1,9 @@ /* * VHT handling * + * Portions of this file + * Copyright(c) 2015 Intel Deutschland GmbH + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -425,6 +428,28 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, return changed; } +void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt) +{ + struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; + + if (!(sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER)) + return; + + if (!memcmp(mgmt->u.action.u.vht_group_notif.position, + bss_conf->mu_group.position, WLAN_USER_POSITION_LEN) && + !memcmp(mgmt->u.action.u.vht_group_notif.membership, + bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN)) + return; + + memcpy(mgmt->u.action.u.vht_group_notif.membership, + bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN); + memcpy(mgmt->u.action.u.vht_group_notif.position, + bss_conf->mu_group.position, WLAN_USER_POSITION_LEN); + + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); +} + void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, u8 opmode, enum ieee80211_band band) -- cgit From f9cfa5f354b11e56cd8f019c12e14a42585586cd Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 8 Dec 2015 16:04:33 +0200 Subject: mac80211: add flag for duplication check Add an option for driver to check for packet duplication by itself. This is needed for example by the iwlwifi driver which parallelizes the RX path and does the duplication check per queue. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 +- net/mac80211/rx.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8da483b2c067..ecab934dc8d9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1063,7 +1063,7 @@ enum mac80211_rx_flags { RX_FLAG_HT_GF = BIT(13), RX_FLAG_AMPDU_DETAILS = BIT(14), RX_FLAG_PN_VALIDATED = BIT(15), - /* bit 16 free */ + RX_FLAG_DUP_VALIDATED = BIT(16), RX_FLAG_AMPDU_LAST_KNOWN = BIT(17), RX_FLAG_AMPDU_IS_LAST = BIT(18), RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a5668b54015f..fe675d76f29c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1099,6 +1099,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + if (status->flag & RX_FLAG_DUP_VALIDATED) + return RX_CONTINUE; + /* * Drop duplicate 802.11 retransmissions * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") -- cgit From fad471860c097844432c7cf5d3ae6a0a059c2bdc Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 8 Dec 2015 16:04:34 +0200 Subject: mac80211: pass RX aggregation window size to driver Currently mac80211 does not inform the driver of the window size when starting an RX aggregation session. To enable managing the reorder buffer in the driver or hardware the window size is needed. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 8 +++++--- net/mac80211/agg-rx.c | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ecab934dc8d9..a990338a766e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3047,9 +3047,11 @@ enum ieee80211_reconfig_type { * ieee80211_ampdu_mlme_action. Starting sequence number (@ssn) * is the first frame we expect to perform the action on. Notice * that TX/RX_STOP can pass NULL for this parameter. - * The @buf_size parameter is only valid when the action is set to - * %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder - * buffer size (number of subframes) for this session -- the driver + * The @buf_size parameter is valid only when the action is set to + * %IEEE80211_AMPDU_RX_START or %IEEE80211_AMPDU_TX_OPERATIONAL and + * indicates the reorder buffer size (number of subframes) for this + * session. + * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver * may neither send aggregates containing more subframes than this * nor send aggregates in a way that lost frames would exceed the * buffer size. If just limiting the aggregate size, this would be diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 10ad4ac1fa0b..78672737fe3e 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -323,7 +323,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]); ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, - &sta->sta, tid, &start_seq_num, 0, false); + &sta->sta, tid, &start_seq_num, buf_size, false); ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", sta->sta.addr, tid, ret); if (ret) { -- cgit From 4352a4d7f6bfd0aed0276a13fa4993db35714db4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Dec 2015 16:04:35 +0200 Subject: mac80211: document status.freq restrictions It's not always necessary to set the status.freq field, for example when this would be an expensive calculation. It must be set for all management frames (as they might be reported to userspace), but for data frames it's not really required. Document this. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a990338a766e..bdee1cc19c7e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1108,6 +1108,8 @@ enum mac80211_rx_vht_flags { * it but can store it and pass it back to the driver for synchronisation * @band: the active band when this frame was received * @freq: frequency the radio was tuned to when receiving this frame, in MHz + * This field must be set for management frames, but isn't strictly needed + * for data (other) frames - for those it only affects radiotap reporting. * @signal: signal strength when receiving this frame, either in dBm, in dB or * unspecified depending on the hardware capabilities flags * @IEEE80211_HW_SIGNAL_* -- cgit From a85a7e28f45f7217b9a2efc3ba323de5c0e5b056 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Fri, 1 Jan 2016 23:48:52 +0800 Subject: cfg80211/mac80211: use to_delayed_work Use to_delayed_work() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 3 +-- net/wireless/mlme.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 31d5881b31fa..4af9b2bcc020 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1638,8 +1638,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data) void ieee80211_dfs_cac_timer_work(struct work_struct *work) { - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); + struct delayed_work *delayed_work = to_delayed_work(work); struct ieee80211_sub_if_data *sdata = container_of(delayed_work, struct ieee80211_sub_if_data, dfs_cac_timer_work); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index fb44fa3bf4ef..ff328250bc44 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -711,7 +711,7 @@ EXPORT_SYMBOL(cfg80211_rx_mgmt); void cfg80211_dfs_channels_update_work(struct work_struct *work) { - struct delayed_work *delayed_work; + struct delayed_work *delayed_work = to_delayed_work(work); struct cfg80211_registered_device *rdev; struct cfg80211_chan_def chandef; struct ieee80211_supported_band *sband; @@ -721,7 +721,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) unsigned long timeout, next_time = 0; int bandid, i; - delayed_work = container_of(work, struct delayed_work, work); rdev = container_of(delayed_work, struct cfg80211_registered_device, dfs_update_channels_wk); wiphy = &rdev->wiphy; -- cgit From 50ea05efaf3bed7dd34bcc2635a8b3f53bd0ccc1 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 30 Dec 2015 16:06:04 +0200 Subject: mac80211: pass block ack session timeout to to driver Currently mac80211 does not inform the driver of the session block ack timeout when starting a rx aggregation session. Drivers that manage the reorder buffer need to know this parameter. Seeing that there are now too many arguments for the drv_ampdu_action() function, wrap them inside a structure. Signed-off-by: Sara Sharon Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 7 +-- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 7 +-- drivers/net/wireless/ath/ath9k/main.c | 8 ++-- drivers/net/wireless/ath/carl9170/main.c | 8 ++-- drivers/net/wireless/ath/wcn36xx/main.c | 8 ++-- .../broadcom/brcm80211/brcmsmac/mac80211_if.c | 8 ++-- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 8 ++-- drivers/net/wireless/intel/iwlegacy/4965.h | 4 +- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 9 ++-- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 9 ++-- drivers/net/wireless/mac80211_hwsim.c | 8 ++-- drivers/net/wireless/marvell/mwl8k.c | 10 ++-- drivers/net/wireless/mediatek/mt7601u/main.c | 8 ++-- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 7 +-- drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 4 +- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c | 6 +-- drivers/net/wireless/realtek/rtlwifi/core.c | 8 ++-- drivers/net/wireless/rsi/rsi_91x_mac80211.c | 19 +++----- drivers/net/wireless/st/cw1200/sta.c | 4 +- drivers/net/wireless/st/cw1200/sta.h | 4 +- drivers/net/wireless/ti/wlcore/main.c | 8 ++-- include/net/mac80211.h | 44 ++++++++++++------ net/mac80211/agg-rx.c | 25 ++++++++-- net/mac80211/agg-tx.c | 53 ++++++++++++++-------- net/mac80211/driver-ops.c | 10 ++-- net/mac80211/driver-ops.h | 4 +- net/mac80211/trace.h | 43 ++++++++++-------- 27 files changed, 202 insertions(+), 139 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 6146a293601a..368de5e5a04f 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6366,12 +6366,13 @@ static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n", arvif->vdev_id, sta->addr, tid, action); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index fe1fd1a5ae15..639294a9e34d 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1657,13 +1657,14 @@ static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw, static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c1b33fdcca08..cf58a304e9f0 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1864,14 +1864,16 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); bool flush = false; int ret = 0; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; mutex_lock(&sc->mutex); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 19d3d64416bf..4d1527a2e292 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1413,10 +1413,12 @@ static void carl9170_ampdu_work(struct work_struct *work) static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; struct carl9170_sta_tid *tid_info; diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7c169abdbafe..a27279c2c695 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -857,12 +857,14 @@ static int wcn36xx_resume(struct ieee80211_hw *hw) static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct wcn36xx *wcn = hw->priv; struct wcn36xx_sta *sta_priv = NULL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", action, tid); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index bec2dc1ca2e4..61ae2768132a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -818,13 +818,15 @@ brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, static int brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; int status; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u8 buf_size = params->buf_size; if (WARN_ON(scb->magic != SCB_MAGIC)) return -EIDRM; diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index fd38aa0763e4..b75f4ef3cdc7 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -5982,12 +5982,14 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct il_priv *il = hw->priv; int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); diff --git a/drivers/net/wireless/intel/iwlegacy/4965.h b/drivers/net/wireless/intel/iwlegacy/4965.h index 8ab8706f9422..e432715e02d8 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965.h +++ b/drivers/net/wireless/intel/iwlegacy/4965.h @@ -182,9 +182,7 @@ void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 29ea1c6705b4..151721e4040c 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -732,12 +732,15 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d70a1716f3e0..1bd3f0b700d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -837,13 +837,16 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; bool tx_agg_ref = false; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", sta->addr, tid, action); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index c32889a1e39c..e31a94fd6135 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1845,10 +1845,12 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + switch (action) { case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index 30e3aaae32e2..088429d0a634 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -5421,11 +5421,13 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, static int mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { - + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; int i, rc = 0; struct mwl8k_priv *priv = hw->priv; struct mwl8k_ampdu_stream *stream; diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c index f715eee39851..e70dd9523911 100644 --- a/drivers/net/wireless/mediatek/mt7601u/main.c +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -334,11 +334,13 @@ static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value) static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, - bool amsdu) + struct ieee80211_ampdu_params *params) { struct mt7601u_dev *dev = hw->priv; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; WARN_ON(msta->wcid.idx > GROUP_WCID(0)); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 9733b31a780d..69c1c09687a3 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -7935,10 +7935,11 @@ u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) EXPORT_SYMBOL_GPL(rt2800_get_tsf); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; int ret = 0; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 440790b92b19..83f1a44fb9b4 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -218,9 +218,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, const struct ieee80211_tx_queue_params *params); u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c index 6aed923a709a..7d820c395375 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c @@ -5375,13 +5375,13 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, static int rtl8xxxu_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, - bool amsdu) + struct ieee80211_ampdu_params *params) { struct rtl8xxxu_priv *priv = hw->priv; struct device *dev = &priv->udev->dev; u8 ampdu_factor, ampdu_density; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; switch (action) { case IEEE80211_AMPDU_TX_START: diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index 4ae421ef30d9..f2507610314b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1371,11 +1371,13 @@ static void rtl_op_sta_notify(struct ieee80211_hw *hw, static int rtl_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; switch (action) { case IEEE80211_AMPDU_TX_START: diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index b5bcc933a2a6..4df992de7d07 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -659,29 +659,24 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw, * informs the f/w regarding this. * @hw: Pointer to the ieee80211_hw structure. * @vif: Pointer to the ieee80211_vif structure. - * @action: ieee80211_ampdu_mlme_action enum. - * @sta: Pointer to the ieee80211_sta structure. - * @tid: Traffic identifier. - * @ssn: Pointer to ssn value. - * @buf_size: Buffer size (for kernel version > 2.6.38). - * @amsdu: is AMSDU in AMPDU allowed + * @params: Pointer to A-MPDU action parameters * * Return: status: 0 on success, negative error code on failure. */ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - unsigned short tid, - unsigned short *ssn, - unsigned char buf_size, - bool amsdu) + struct ieee80211_ampdu_params *params) { int status = -EOPNOTSUPP; struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; u16 seq_no = 0; u8 ii = 0; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; for (ii = 0; ii < RSI_MAX_VIFS; ii++) { if (vif == adapter->vifs[ii]) diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c index 06321c799c90..d0ddcde6c695 100644 --- a/drivers/net/wireless/st/cw1200/sta.c +++ b/drivers/net/wireless/st/cw1200/sta.c @@ -2129,9 +2129,7 @@ void cw1200_mcast_timeout(unsigned long arg) int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/net/wireless/st/cw1200/sta.h b/drivers/net/wireless/st/cw1200/sta.h index bebb3379017f..a0bacaa39b31 100644 --- a/drivers/net/wireless/st/cw1200/sta.h +++ b/drivers/net/wireless/st/cw1200/sta.h @@ -109,9 +109,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, u32 changed); int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d1109c4f0f0d..45662cf3169f 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5187,14 +5187,16 @@ out: static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; u8 hlid, *ba_bitmap; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action, tid); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index bdee1cc19c7e..6c9c559394b0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2702,6 +2702,33 @@ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_TX_OPERATIONAL, }; +/** + * struct ieee80211_ampdu_params - AMPDU action parameters + * + * @action: the ampdu action, value from %ieee80211_ampdu_mlme_action. + * @sta: peer of this AMPDU session + * @tid: tid of the BA session + * @ssn: start sequence number of the session. TX/RX_STOP can pass 0. When + * action is set to %IEEE80211_AMPDU_RX_START the driver passes back the + * actual ssn value used to start the session and writes the value here. + * @buf_size: reorder buffer size (number of subframes). Valid only when the + * action is set to %IEEE80211_AMPDU_RX_START or + * %IEEE80211_AMPDU_TX_OPERATIONAL + * @amsdu: indicates the peer's ability to receive A-MSDU within A-MPDU. + * valid when the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL + * @timeout: BA session timeout. Valid only when the action is set to + * %IEEE80211_AMPDU_RX_START + */ +struct ieee80211_ampdu_params { + enum ieee80211_ampdu_mlme_action action; + struct ieee80211_sta *sta; + u16 tid; + u16 ssn; + u8 buf_size; + bool amsdu; + u16 timeout; +}; + /** * enum ieee80211_frame_release_type - frame release reason * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll @@ -3046,15 +3073,9 @@ enum ieee80211_reconfig_type { * @ampdu_action: Perform a certain A-MPDU action * The RA/TID combination determines the destination and TID we want * the ampdu action to be performed for. The action is defined through - * ieee80211_ampdu_mlme_action. Starting sequence number (@ssn) - * is the first frame we expect to perform the action on. Notice - * that TX/RX_STOP can pass NULL for this parameter. - * The @buf_size parameter is valid only when the action is set to - * %IEEE80211_AMPDU_RX_START or %IEEE80211_AMPDU_TX_OPERATIONAL and - * indicates the reorder buffer size (number of subframes) for this - * session. + * ieee80211_ampdu_mlme_action. * When the action is set to %IEEE80211_AMPDU_TX_OPERATIONAL the driver - * may neither send aggregates containing more subframes than this + * may neither send aggregates containing more subframes than @buf_size * nor send aggregates in a way that lost frames would exceed the * buffer size. If just limiting the aggregate size, this would be * possible with a buf_size of 8: @@ -3065,9 +3086,6 @@ enum ieee80211_reconfig_type { * buffer size of 8. Correct ways to retransmit #1 would be: * - TX: 1 or 18 or 81 * Even "189" would be wrong since 1 could be lost again. - * The @amsdu parameter is valid when the action is set to - * %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's ability - * to receive A-MSDU within A-MPDU. * * Returns a negative error code on failure. * The callback can sleep. @@ -3409,9 +3427,7 @@ struct ieee80211_ops { int (*tx_last_beacon)(struct ieee80211_hw *hw); int (*ampdu_action)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); int (*get_survey)(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void (*rfkill_poll)(struct ieee80211_hw *hw); diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 78672737fe3e..ec80db7c955c 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -7,6 +7,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -61,6 +62,14 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, { struct ieee80211_local *local = sta->local; struct tid_ampdu_rx *tid_rx; + struct ieee80211_ampdu_params params = { + .sta = &sta->sta, + .action = IEEE80211_AMPDU_RX_STOP, + .tid = tid, + .amsdu = false, + .timeout = 0, + .ssn = 0, + }; lockdep_assert_held(&sta->ampdu_mlme.mtx); @@ -78,8 +87,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", (int)reason); - if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, - &sta->sta, tid, NULL, 0, false)) + if (drv_ampdu_action(local, sta->sdata, ¶ms)) sdata_info(sta->sdata, "HW problem - can not stop rx aggregation for %pM tid %d\n", sta->sta.addr, tid); @@ -237,6 +245,15 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, { struct ieee80211_local *local = sta->sdata->local; struct tid_ampdu_rx *tid_agg_rx; + struct ieee80211_ampdu_params params = { + .sta = &sta->sta, + .action = IEEE80211_AMPDU_RX_START, + .tid = tid, + .amsdu = false, + .timeout = timeout, + .ssn = start_seq_num, + }; + int i, ret = -EOPNOTSUPP; u16 status = WLAN_STATUS_REQUEST_DECLINED; @@ -275,6 +292,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, /* make sure the size doesn't exceed the maximum supported by the hw */ if (buf_size > local->hw.max_rx_aggregation_subframes) buf_size = local->hw.max_rx_aggregation_subframes; + params.buf_size = buf_size; /* examine state machine */ mutex_lock(&sta->ampdu_mlme.mtx); @@ -322,8 +340,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, for (i = 0; i < buf_size; i++) __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]); - ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, - &sta->sta, tid, &start_seq_num, buf_size, false); + ret = drv_ampdu_action(local, sta->sdata, ¶ms); ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", sta->sta.addr, tid, ret); if (ret) { diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index ff757181b0a8..4932e9f243a2 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -7,6 +7,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -295,7 +296,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; - enum ieee80211_ampdu_mlme_action action; + struct ieee80211_ampdu_params params = { + .sta = &sta->sta, + .tid = tid, + .buf_size = 0, + .amsdu = false, + .timeout = 0, + .ssn = 0, + }; int ret; lockdep_assert_held(&sta->ampdu_mlme.mtx); @@ -304,10 +312,10 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, case AGG_STOP_DECLINED: case AGG_STOP_LOCAL_REQUEST: case AGG_STOP_PEER_REQUEST: - action = IEEE80211_AMPDU_TX_STOP_CONT; + params.action = IEEE80211_AMPDU_TX_STOP_CONT; break; case AGG_STOP_DESTROY_STA: - action = IEEE80211_AMPDU_TX_STOP_FLUSH; + params.action = IEEE80211_AMPDU_TX_STOP_FLUSH; break; default: WARN_ON_ONCE(1); @@ -330,9 +338,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, spin_unlock_bh(&sta->lock); if (reason != AGG_STOP_DESTROY_STA) return -EALREADY; - ret = drv_ampdu_action(local, sta->sdata, - IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, - &sta->sta, tid, NULL, 0, false); + params.action = IEEE80211_AMPDU_TX_STOP_FLUSH_CONT; + ret = drv_ampdu_action(local, sta->sdata, ¶ms); WARN_ON_ONCE(ret); return 0; } @@ -381,8 +388,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, WLAN_BACK_INITIATOR; tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; - ret = drv_ampdu_action(local, sta->sdata, action, - &sta->sta, tid, NULL, 0, false); + ret = drv_ampdu_action(local, sta->sdata, ¶ms); /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { @@ -445,7 +451,14 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) struct tid_ampdu_tx *tid_tx; struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - u16 start_seq_num; + struct ieee80211_ampdu_params params = { + .sta = &sta->sta, + .action = IEEE80211_AMPDU_TX_START, + .tid = tid, + .buf_size = 0, + .amsdu = false, + .timeout = 0, + }; int ret; tid_tx = rcu_dereference_protected_tid_tx(sta, tid); @@ -467,10 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) */ synchronize_net(); - start_seq_num = sta->tid_seq[tid] >> 4; - - ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, - &sta->sta, tid, &start_seq_num, 0, false); + params.ssn = sta->tid_seq[tid] >> 4; + ret = drv_ampdu_action(local, sdata, ¶ms); if (ret) { ht_dbg(sdata, "BA request denied - HW unavailable for %pM tid %d\n", @@ -499,7 +510,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) /* send AddBA request */ ieee80211_send_addba_request(sdata, sta->sta.addr, tid, - tid_tx->dialog_token, start_seq_num, + tid_tx->dialog_token, params.ssn, IEEE80211_MAX_AMPDU_BUF, tid_tx->timeout); } @@ -684,18 +695,24 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { struct tid_ampdu_tx *tid_tx; + struct ieee80211_ampdu_params params = { + .sta = &sta->sta, + .action = IEEE80211_AMPDU_TX_OPERATIONAL, + .tid = tid, + .timeout = 0, + .ssn = 0, + }; lockdep_assert_held(&sta->ampdu_mlme.mtx); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + params.buf_size = tid_tx->buf_size; + params.amsdu = tid_tx->amsdu; ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n", sta->sta.addr, tid); - drv_ampdu_action(local, sta->sdata, - IEEE80211_AMPDU_TX_OPERATIONAL, - &sta->sta, tid, NULL, tid_tx->buf_size, - tid_tx->amsdu); + drv_ampdu_action(local, sta->sdata, ¶ms); /* * synchronize with TX path, while splicing the TX path diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index ca1fe5576103..c258f1041d33 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -284,9 +284,7 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local, int drv_ampdu_action(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { int ret = -EOPNOTSUPP; @@ -296,12 +294,10 @@ int drv_ampdu_action(struct ieee80211_local *local, if (!check_sdata_in_driver(sdata)) return -EIO; - trace_drv_ampdu_action(local, sdata, action, sta, tid, - ssn, buf_size, amsdu); + trace_drv_ampdu_action(local, sdata, params); if (local->ops->ampdu_action) - ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, - sta, tid, ssn, buf_size, amsdu); + ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params); trace_drv_return_int(local, ret); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 154ce4b13406..18b0d65baff0 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -585,9 +585,7 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local) int drv_ampdu_action(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); static inline int drv_get_survey(struct ieee80211_local *local, int idx, struct survey_info *survey) diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a6b4442776a0..2b0a17ee907a 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -80,7 +80,23 @@ #define KEY_PR_FMT " cipher:0x%x, flags=%#x, keyidx=%d, hw_key_idx=%d" #define KEY_PR_ARG __entry->cipher, __entry->flags, __entry->keyidx, __entry->hw_key_idx - +#define AMPDU_ACTION_ENTRY __field(enum ieee80211_ampdu_mlme_action, \ + ieee80211_ampdu_mlme_action) \ + STA_ENTRY \ + __field(u16, tid) \ + __field(u16, ssn) \ + __field(u8, buf_size) \ + __field(bool, amsdu) \ + __field(u16, timeout) +#define AMPDU_ACTION_ASSIGN STA_NAMED_ASSIGN(params->sta); \ + __entry->tid = params->tid; \ + __entry->ssn = params->ssn; \ + __entry->buf_size = params->buf_size; \ + __entry->amsdu = params->amsdu; \ + __entry->timeout = params->timeout; +#define AMPDU_ACTION_PR_FMT STA_PR_FMT " tid %d, ssn %d, buf_size %u, amsdu %d, timeout %d" +#define AMPDU_ACTION_PR_ARG STA_PR_ARG, __entry->tid, __entry->ssn, \ + __entry->buf_size, __entry->amsdu, __entry->timeout /* * Tracing for driver callbacks. @@ -970,38 +986,25 @@ DEFINE_EVENT(local_only_evt, drv_tx_last_beacon, TRACE_EVENT(drv_ampdu_action, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu), + struct ieee80211_ampdu_params *params), - TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size, amsdu), + TP_ARGS(local, sdata, params), TP_STRUCT__entry( LOCAL_ENTRY - STA_ENTRY - __field(u32, action) - __field(u16, tid) - __field(u16, ssn) - __field(u8, buf_size) - __field(bool, amsdu) VIF_ENTRY + AMPDU_ACTION_ENTRY ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; - STA_ASSIGN; - __entry->action = action; - __entry->tid = tid; - __entry->ssn = ssn ? *ssn : 0; - __entry->buf_size = buf_size; - __entry->amsdu = amsdu; + AMPDU_ACTION_ASSIGN; ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d amsdu:%d", - LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, - __entry->tid, __entry->buf_size, __entry->amsdu + LOCAL_PR_FMT VIF_PR_FMT AMPDU_ACTION_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG, AMPDU_ACTION_PR_ARG ) ); -- cgit From 412a6d800c7380c1b87c11080c7da905c27cfea8 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 8 Dec 2015 19:09:05 +0200 Subject: mac80211: support hw managing reorder logic Enable driver to manage the reordering logic itself. This is needed for example for the iwlwifi driver that will support hardware assisted reordering. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 6 ++++++ net/mac80211/agg-rx.c | 24 ++++++++++++++++++++++-- net/mac80211/debugfs.c | 1 + net/mac80211/sta_info.h | 21 ++++++++++++--------- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6c9c559394b0..ee6305a52251 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1929,6 +1929,11 @@ struct ieee80211_txq { * by just its MAC address; this prevents, for example, the same station * from connecting to two virtual AP interfaces at the same time. * + * @IEEE80211_HW_SUPPORTS_REORDERING_BUFFER: Hardware (or driver) manages the + * reordering buffer internally, guaranteeing mac80211 receives frames in + * order and does not need to manage its own reorder buffer or BA session + * timeout. + * * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays */ enum ieee80211_hw_flags { @@ -1965,6 +1970,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU, IEEE80211_HW_BEACON_TX_STATUS, IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR, + IEEE80211_HW_SUPPORTS_REORDERING_BUFFER, /* keep last, obviously */ NUM_IEEE80211_HW_FLAGS diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index ec80db7c955c..2ab54791281d 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -76,10 +76,11 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid], lockdep_is_held(&sta->ampdu_mlme.mtx)); - if (!tid_rx) + if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid)) return; RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); + __clear_bit(tid, sta->ampdu_mlme.agg_session_valid); ht_dbg(sta->sdata, "Rx BA session stop requested for %pM tid %u %s reason: %d\n", @@ -97,6 +98,13 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, ieee80211_send_delba(sta->sdata, sta->sta.addr, tid, WLAN_BACK_RECIPIENT, reason); + /* + * return here in case tid_rx is not assigned - which will happen if + * IEEE80211_HW_SUPPORTS_REORDERING_BUFFER is set. + */ + if (!tid_rx) + return; + del_timer_sync(&tid_rx->session_timer); /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */ @@ -297,7 +305,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, /* examine state machine */ mutex_lock(&sta->ampdu_mlme.mtx); - if (sta->ampdu_mlme.tid_rx[tid]) { + if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { ht_dbg_ratelimited(sta->sdata, "unexpected AddBA Req from %pM on tid %u\n", sta->sta.addr, tid); @@ -308,6 +316,16 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, false); } + if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) { + ret = drv_ampdu_action(local, sta->sdata, ¶ms); + ht_dbg(sta->sdata, + "Rx A-MPDU request on %pM tid %d result %d\n", + sta->sta.addr, tid, ret); + if (!ret) + status = WLAN_STATUS_SUCCESS; + goto end; + } + /* prepare A-MPDU MLME for Rx aggregation */ tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL); if (!tid_agg_rx) @@ -369,6 +387,8 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, } end: + if (status == WLAN_STATUS_SUCCESS) + __set_bit(tid, sta->ampdu_mlme.agg_session_valid); mutex_unlock(&sta->ampdu_mlme.mtx); end_no_lock: diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index abbdff03ce92..e433d0c97e86 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -126,6 +126,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = { FLAG(SUPPORTS_AMSDU_IN_AMPDU), FLAG(BEACON_TX_STATUS), FLAG(NEEDS_UNIQUE_STA_ADDR), + FLAG(SUPPORTS_REORDERING_BUFFER), /* keep last for the build bug below */ (void *)0x1 diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index d6051629ed15..f4d38994ecee 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -1,6 +1,7 @@ /* * Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -212,20 +213,21 @@ struct tid_ampdu_rx { /** * struct sta_ampdu_mlme - STA aggregation information. * + * @mtx: mutex to protect all TX data (except non-NULL assignments + * to tid_tx[idx], which are protected by the sta spinlock) + * tid_start_tx is also protected by sta->lock. * @tid_rx: aggregation info for Rx per TID -- RCU protected - * @tid_tx: aggregation info for Tx per TID - * @tid_start_tx: sessions where start was requested - * @addba_req_num: number of times addBA request has been sent. - * @last_addba_req_time: timestamp of the last addBA request. - * @dialog_token_allocator: dialog token enumerator for each new session; - * @work: work struct for starting/stopping aggregation * @tid_rx_timer_expired: bitmap indicating on which TIDs the * RX timer expired until the work for it runs * @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the * driver requested to close until the work for it runs - * @mtx: mutex to protect all TX data (except non-NULL assignments - * to tid_tx[idx], which are protected by the sta spinlock) - * tid_start_tx is also protected by sta->lock. + * @agg_session_valid: bitmap indicating which TID has a rx BA session open on + * @work: work struct for starting/stopping aggregation + * @tid_tx: aggregation info for Tx per TID + * @tid_start_tx: sessions where start was requested + * @last_addba_req_time: timestamp of the last addBA request. + * @addba_req_num: number of times addBA request has been sent. + * @dialog_token_allocator: dialog token enumerator for each new session; */ struct sta_ampdu_mlme { struct mutex mtx; @@ -233,6 +235,7 @@ struct sta_ampdu_mlme { struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; + unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; /* tx */ struct work_struct work; struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; -- cgit From 178830481eee5eea147a1c8fab67a96e09d80345 Mon Sep 17 00:00:00 2001 From: Grzegorz Bajorski Date: Fri, 11 Dec 2015 14:39:46 +0100 Subject: mac80211: allow drivers to report (non-)monitor frames Some drivers offload some frames internally (e.g. AddBa). Reporting such frames to mac80211 would only confuse MLME. However it would be useful to be able to pass such frames to monitor interfaces for sniffing purposes, e.g. when running AP + monitor. To do that allow drivers to tell mac80211 whether a given frame should be: - processed but not delivered to any monitor vif - not processed but delievered to monitor vifs only Signed-off-by: Grzegorz Bajorski Signed-off-by: Johannes Berg --- include/net/mac80211.h | 11 +++++++++++ net/mac80211/rx.c | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ee6305a52251..5910085af9e6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1031,6 +1031,14 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * is stored in the @ampdu_delimiter_crc field) * @RX_FLAG_LDPC: LDPC was used + * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without + * processing it in any regular way. + * This is useful if drivers offload some frames but still want to report + * them for sniffing purposes. + * @RX_FLAG_SKIP_MONITOR: Process and report frame to all interfaces except + * monitor interfaces. + * This is useful if drivers offload some frames but still want to report + * them for sniffing purposes. * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 * @RX_FLAG_10MHZ: 10 MHz (half channel) was used * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used @@ -1071,6 +1079,8 @@ enum mac80211_rx_flags { RX_FLAG_MACTIME_END = BIT(21), RX_FLAG_VHT = BIT(22), RX_FLAG_LDPC = BIT(23), + RX_FLAG_ONLY_MONITOR = BIT(24), + RX_FLAG_SKIP_MONITOR = BIT(25), RX_FLAG_STBC_MASK = BIT(26) | BIT(27), RX_FLAG_10MHZ = BIT(28), RX_FLAG_5MHZ = BIT(29), @@ -1089,6 +1099,7 @@ enum mac80211_rx_flags { * @RX_VHT_FLAG_160MHZ: 160 MHz was used * @RX_VHT_FLAG_BF: packet was beamformed */ + enum mac80211_rx_vht_flags { RX_VHT_FLAG_80MHZ = BIT(0), RX_VHT_FLAG_160MHZ = BIT(1), diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fe675d76f29c..ae993edfdecf 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -122,7 +122,8 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len, hdr = (void *)(skb->data + rtap_vendor_space); if (status->flag & (RX_FLAG_FAILED_FCS_CRC | - RX_FLAG_FAILED_PLCP_CRC)) + RX_FLAG_FAILED_PLCP_CRC | + RX_FLAG_ONLY_MONITOR)) return true; if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space)) @@ -507,7 +508,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return NULL; } - if (!local->monitors) { + if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) { if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) { dev_kfree_skb(origskb); -- cgit From a7201a6c5ea0157e96d17240d185a538bfdb7038 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 13 Dec 2015 13:41:43 +0200 Subject: mac80211: Recalc min chandef when station is associated The minimum chandef bandwidth calculation was done only in case a new station was inserted (or when an existing station was removed). However, it is possible that stations are inserted before they are associated, e.g., when FULL_AP_CLIENT_STATE is supported and user space adds stations unassociated. Fix this by calling ieee80211_recalc_min_chandef() whenever a station transitions in/out the associated state, and only consider station marked as associated. Signed-off-by: Ilan Peer Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 2 +- net/mac80211/sta_info.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 1d1b9b7bdefe..283981108ca8 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -231,7 +231,7 @@ ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) continue; - if (!sta->uploaded) + if (!sta->uploaded || !test_sta_flag(sta, WLAN_STA_ASSOC)) continue; max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4402ad5b27d1..7e007cf12cb2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -530,7 +530,6 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) /* accept BA sessions now */ clear_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_recalc_min_chandef(sdata); ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); @@ -941,7 +940,6 @@ static void __sta_info_destroy_part2(struct sta_info *sta) rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); - ieee80211_recalc_min_chandef(sdata); cleanup_single_sta(sta); } @@ -1808,14 +1806,17 @@ int sta_info_move_state(struct sta_info *sta, clear_bit(WLAN_STA_AUTH, &sta->_flags); break; case IEEE80211_STA_AUTH: - if (sta->sta_state == IEEE80211_STA_NONE) + if (sta->sta_state == IEEE80211_STA_NONE) { set_bit(WLAN_STA_AUTH, &sta->_flags); - else if (sta->sta_state == IEEE80211_STA_ASSOC) + } else if (sta->sta_state == IEEE80211_STA_ASSOC) { clear_bit(WLAN_STA_ASSOC, &sta->_flags); + ieee80211_recalc_min_chandef(sta->sdata); + } break; case IEEE80211_STA_ASSOC: if (sta->sta_state == IEEE80211_STA_AUTH) { set_bit(WLAN_STA_ASSOC, &sta->_flags); + ieee80211_recalc_min_chandef(sta->sdata); } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { if (sta->sdata->vif.type == NL80211_IFTYPE_AP || (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && -- cgit From 506bcfa8abebdbcebdc17b03e96e38dc0b8ce765 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 13 Dec 2015 15:41:05 +0200 Subject: mac80211: limit the A-MSDU Tx based on peer's capabilities In VHT, the specification allows to limit the number of MSDUs in an A-MSDU in the Extended Capabilities IE. There is also a limitation on the byte size in the VHT IE. In HT, the only limitation is on the byte size. Parse the capabilities from the peer and make them available to the driver. In HT, there is another limitation when a BA agreement is active: the byte size can't be greater than 4095. This is not enforced here. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 19 +++++++++++++++++++ include/net/mac80211.h | 14 ++++++++++++++ net/mac80211/cfg.c | 29 +++++++++++++++++++++++++++++ net/mac80211/ht.c | 5 +++++ net/mac80211/vht.c | 17 +++++++++++++++++ 5 files changed, 84 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d9ddb89533a7..3b1f6cef9513 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -163,6 +163,14 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2) /* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */ #define IEEE80211_MAX_FRAME_LEN 2352 +/* Maximal size of an A-MSDU */ +#define IEEE80211_MAX_MPDU_LEN_HT_3839 3839 +#define IEEE80211_MAX_MPDU_LEN_HT_7935 7935 + +#define IEEE80211_MAX_MPDU_LEN_VHT_3895 3895 +#define IEEE80211_MAX_MPDU_LEN_VHT_7991 7991 +#define IEEE80211_MAX_MPDU_LEN_VHT_11454 11454 + #define IEEE80211_MAX_SSID_LEN 32 #define IEEE80211_MAX_MESH_ID_LEN 32 @@ -1505,6 +1513,7 @@ struct ieee80211_vht_operation { #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002 +#define IEEE80211_VHT_CAP_MAX_MPDU_MASK 0x00000003 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000C @@ -2086,6 +2095,16 @@ enum ieee80211_tdls_actioncode { #define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(5) #define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6) +/* Defines the maximal number of MSDUs in an A-MSDU. */ +#define WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB BIT(7) +#define WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB BIT(0) + +/* + * Fine Timing Measurement Initiator - bit 71 of @WLAN_EID_EXT_CAPABILITY + * information element + */ +#define WLAN_EXT_CAPA9_FTM_INITIATOR BIT(7) + /* TDLS specific payload type in the LLC/SNAP header */ #define WLAN_TDLS_SNAP_RFTYPE 0x2 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5910085af9e6..df5698ed8052 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1714,6 +1714,18 @@ struct ieee80211_sta_rates { * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only * valid if the STA is a TDLS peer in the first place. * @mfp: indicates whether the STA uses management frame protection or not. + * @max_amsdu_subframes: indicates the maximal number of MSDUs in a single + * A-MSDU. Taken from the Extended Capabilities element. 0 means + * unlimited. + * @max_amsdu_len: indicates the maximal length of an A-MSDU in bytes. This + * field is always valid for packets with a VHT preamble. For packets + * with a HT preamble, additional limits apply: + * + If the skb is transmitted as part of a BA agreement, the + * A-MSDU maximal size is min(max_amsdu_len, 4065) bytes. + * + If the skb is not part of a BA aggreement, the A-MSDU maximal + * size is min(max_amsdu_len, 7935) bytes. + * Both additional HT limits must be enforced by the low level driver. + * This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2). * @txq: per-TID data TX queues (if driver uses the TXQ abstraction) */ struct ieee80211_sta { @@ -1732,6 +1744,8 @@ struct ieee80211_sta { bool tdls; bool tdls_initiator; bool mfp; + u8 max_amsdu_subframes; + u16 max_amsdu_len; struct ieee80211_txq *txq[IEEE80211_NUM_TIDS]; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 166a29fe6c35..66d22de93c8d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1131,6 +1131,34 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->sta.max_sp = params->max_sp; } + /* The sender might not have sent the last bit, consider it to be 0 */ + if (params->ext_capab_len >= 8) { + u8 val = (params->ext_capab[7] & + WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB) >> 7; + + /* we did get all the bits, take the MSB as well */ + if (params->ext_capab_len >= 9) { + u8 val_msb = params->ext_capab[8] & + WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB; + val_msb <<= 1; + val |= val_msb; + } + + switch (val) { + case 1: + sta->sta.max_amsdu_subframes = 32; + break; + case 2: + sta->sta.max_amsdu_subframes = 16; + break; + case 3: + sta->sta.max_amsdu_subframes = 8; + break; + default: + sta->sta.max_amsdu_subframes = 0; + } + } + /* * cfg80211 validates this (1-2007) and allows setting the AID * only when creating a new station entry @@ -1160,6 +1188,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, params->ht_capa, sta); + /* VHT can override some HT caps such as the A-MSDU max length */ if (params->vht_capa) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, params->vht_capa, sta); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 7a76ce639d58..f4a528773563 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -230,6 +230,11 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, /* set Rx highest rate */ ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest; + if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU) + sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935; + else + sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839; + apply: changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 050de08bf82e..204cf9ad3019 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -281,6 +281,23 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, } sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); + + /* If HT IE reported 3839 bytes only, stay with that size. */ + if (sta->sta.max_amsdu_len == IEEE80211_MAX_MPDU_LEN_HT_3839) + return; + + switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { + case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: + sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; + break; + case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: + sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; + break; + case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: + default: + sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; + break; + } } enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta) -- cgit From de3bb771f471ba7c745a913b95eee70d5fb199b1 Mon Sep 17 00:00:00 2001 From: Ola Olsson Date: Wed, 16 Dec 2015 22:43:35 +0100 Subject: cfg80211: add more warnings for inconsistent ops Print a warning whenever an expected callback function lacks implementation. Signed-off-by: Ola Olsson Signed-off-by: Johannes Berg --- net/wireless/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/wireless/core.c b/net/wireless/core.c index b0915515640e..3a9c41bc849a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -352,6 +352,16 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, WARN_ON(ops->add_station && !ops->del_station); WARN_ON(ops->add_mpath && !ops->del_mpath); WARN_ON(ops->join_mesh && !ops->leave_mesh); + WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device); + WARN_ON(ops->start_ap && !ops->stop_ap); + WARN_ON(ops->join_ocb && !ops->leave_ocb); + WARN_ON(ops->suspend && !ops->resume); + WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop); + WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel); + WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch); + WARN_ON(ops->add_tx_ts && !ops->del_tx_ts); + WARN_ON(ops->set_tx_power && !ops->get_tx_power); + WARN_ON(ops->set_antenna && !ops->get_antenna); alloc_size = sizeof(*rdev) + sizeof_priv; -- cgit From 8ac3c70419176b0fbc4aeae30de661f690b2e3ae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 Dec 2015 15:08:34 +0100 Subject: mac80211: refactor HT/VHT to chandef code The station MLME and IBSS/mesh ones use entirely different code for interpreting HT and VHT operation elements. Change the code that interprets them a bit - it now modifies an existing chandef - and use it also in the MLME code. Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 10 ++++----- net/mac80211/ieee80211_i.h | 10 ++++----- net/mac80211/mesh.c | 9 ++++---- net/mac80211/mlme.c | 41 ++++------------------------------- net/mac80211/util.c | 54 +++++++++++++++++++++++++--------------------- 5 files changed, 46 insertions(+), 78 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index f7fc0e00497f..e5e8a317b865 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1050,9 +1050,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def chandef; enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth; - ieee80211_ht_oper_to_chandef(channel, - elems->ht_operation, - &chandef); + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); + ieee80211_chandef_ht_oper(elems->ht_operation, &chandef); memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, @@ -1066,9 +1065,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_vht_cap cap_ie; struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap; - ieee80211_vht_oper_to_chandef(channel, - elems->vht_operation, - &chandef); + ieee80211_chandef_vht_oper(elems->vht_operation, + &chandef); memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie)); ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, &cap_ie, sta); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 747402d8c7a9..9934447a8b9a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1988,12 +1988,10 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo); /* channel management */ -void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, - const struct ieee80211_ht_operation *ht_oper, - struct cfg80211_chan_def *chandef); -void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan, - const struct ieee80211_vht_operation *oper, - struct cfg80211_chan_def *chandef); +bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, + struct cfg80211_chan_def *chandef); +bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, + struct cfg80211_chan_def *chandef); u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c); int __must_check diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index fa28500f28fd..9a8e7b57c86e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -91,11 +91,10 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, if (sdata->vif.bss_conf.basic_rates != basic_rates) return false; - ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, - ie->ht_operation, &sta_chan_def); - - ieee80211_vht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, - ie->vht_operation, &sta_chan_def); + cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan, + NL80211_CHAN_NO_HT); + ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def); + ieee80211_chandef_vht_oper(ie->vht_operation, &sta_chan_def); if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, &sta_chan_def)) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4af9b2bcc020..e6d198b981ab 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -196,16 +196,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, /* check 40 MHz support, if we have it */ if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - chandef->width = NL80211_CHAN_WIDTH_40; - chandef->center_freq1 += 10; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - chandef->width = NL80211_CHAN_WIDTH_40; - chandef->center_freq1 -= 10; - break; - } + ieee80211_chandef_ht_oper(ht_oper, chandef); } else { /* 40 MHz (and 80 MHz) must be supported for VHT */ ret = IEEE80211_STA_DISABLE_VHT; @@ -219,35 +210,11 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, goto out; } - vht_chandef.chan = channel; - vht_chandef.center_freq1 = - ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, - channel->band); - vht_chandef.center_freq2 = 0; - - switch (vht_oper->chan_width) { - case IEEE80211_VHT_CHANWIDTH_USE_HT: - vht_chandef.width = chandef->width; - vht_chandef.center_freq1 = chandef->center_freq1; - break; - case IEEE80211_VHT_CHANWIDTH_80MHZ: - vht_chandef.width = NL80211_CHAN_WIDTH_80; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - vht_chandef.width = NL80211_CHAN_WIDTH_160; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - vht_chandef.width = NL80211_CHAN_WIDTH_80P80; - vht_chandef.center_freq2 = - ieee80211_channel_to_frequency( - vht_oper->center_freq_seg2_idx, - channel->band); - break; - default: + vht_chandef = *chandef; + if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) { if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) sdata_info(sdata, - "AP VHT operation IE has invalid channel width (%d), disable VHT\n", - vht_oper->chan_width); + "AP VHT information is invalid, disable VHT\n"); ret = IEEE80211_STA_DISABLE_VHT; goto out; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f4b2c04e7d81..7d0479e31674 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2383,17 +2383,13 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, return pos + sizeof(struct ieee80211_vht_operation); } -void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, - const struct ieee80211_ht_operation *ht_oper, - struct cfg80211_chan_def *chandef) +bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, + struct cfg80211_chan_def *chandef) { enum nl80211_channel_type channel_type; - if (!ht_oper) { - cfg80211_chandef_create(chandef, control_chan, - NL80211_CHAN_NO_HT); - return; - } + if (!ht_oper) + return false; switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_NONE: @@ -2407,42 +2403,52 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, break; default: channel_type = NL80211_CHAN_NO_HT; + return false; } - cfg80211_chandef_create(chandef, control_chan, channel_type); + cfg80211_chandef_create(chandef, chandef->chan, channel_type); + return true; } -void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan, - const struct ieee80211_vht_operation *oper, - struct cfg80211_chan_def *chandef) +bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, + struct cfg80211_chan_def *chandef) { + struct cfg80211_chan_def new = *chandef; + int cf1, cf2; + if (!oper) - return; + return false; - chandef->chan = control_chan; + cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, + chandef->chan->band); + cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg2_idx, + chandef->chan->band); switch (oper->chan_width) { case IEEE80211_VHT_CHANWIDTH_USE_HT: break; case IEEE80211_VHT_CHANWIDTH_80MHZ: - chandef->width = NL80211_CHAN_WIDTH_80; + new.width = NL80211_CHAN_WIDTH_80; + new.center_freq1 = cf1; break; case IEEE80211_VHT_CHANWIDTH_160MHZ: - chandef->width = NL80211_CHAN_WIDTH_160; + new.width = NL80211_CHAN_WIDTH_160; + new.center_freq1 = cf1; break; case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - chandef->width = NL80211_CHAN_WIDTH_80P80; + new.width = NL80211_CHAN_WIDTH_80P80; + new.center_freq1 = cf1; + new.center_freq2 = cf2; break; default: - break; + return false; } - chandef->center_freq1 = - ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, - control_chan->band); - chandef->center_freq2 = - ieee80211_channel_to_frequency(oper->center_freq_seg2_idx, - control_chan->band); + if (!cfg80211_chandef_valid(&new)) + return false; + + *chandef = new; + return true; } int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, -- cgit From 538dc9045251d3d6b5c0216a5c61c32bd9cedac9 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 24 Dec 2015 00:33:26 -0800 Subject: mac80211: Make addr const in SET_IEEE80211_PERM_ADDR() Make the addr parameter const in SET_IEEE80211_PERM_ADDR() to save clients from having to cast away a const qualifier. Signed-off-by: Bjorn Andersson Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index df5698ed8052..566df20dc957 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2217,7 +2217,7 @@ static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev * @hw: the &struct ieee80211_hw to set the MAC address for * @addr: the address to set */ -static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) +static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, const u8 *addr) { memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); } -- cgit From f06b7ab875037c3d99cd30a07c51caf34fbecb2c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jan 2016 14:21:01 +0100 Subject: mac80211_hwsim: remove shadowing variable The function here already has a variable hdr that even contains the right thing, so the inner scope's hdr variable that's shadowing the outer one can just be removed. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index e31a94fd6135..ee37af1066d2 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1333,10 +1333,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, data->tx_bytes += skb->len; ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel); - if (ack && skb->len >= 16) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + if (ack && skb->len >= 16) mac80211_hwsim_monitor_ack(channel, hdr->addr2); - } ieee80211_tx_info_clear_status(txi); -- cgit From dd21dfc645d5dce0657af78761b3fa11a3a95398 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 20 Jan 2016 10:39:23 +0100 Subject: rfkill: disentangle polling pause and suspend When suspended while polling is paused, polling will erroneously resume at resume time. Fix this by tracking pause and suspend in separate state variable and adding the necessary checks. Clarify the documentation on this as well. Signed-off-by: Johannes Berg --- include/linux/rfkill.h | 3 ++- net/rfkill/core.c | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index d9010789b4e8..7af625f6d226 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -104,7 +104,8 @@ int __must_check rfkill_register(struct rfkill *rfkill); * * Pause polling -- say transmitter is off for other reasons. * NOTE: not necessary for suspend/resume -- in that case the - * core stops polling anyway + * core stops polling anyway (but will also correctly handle + * the case of polling having been paused before suspend.) */ void rfkill_pause_polling(struct rfkill *rfkill); diff --git a/net/rfkill/core.c b/net/rfkill/core.c index f53bf3b6558b..166439995f34 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -57,6 +57,8 @@ struct rfkill { bool registered; bool persistent; + bool polling_paused; + bool suspended; const struct rfkill_ops *ops; void *data; @@ -786,6 +788,7 @@ void rfkill_pause_polling(struct rfkill *rfkill) if (!rfkill->ops->poll) return; + rfkill->polling_paused = true; cancel_delayed_work_sync(&rfkill->poll_work); } EXPORT_SYMBOL(rfkill_pause_polling); @@ -797,6 +800,11 @@ void rfkill_resume_polling(struct rfkill *rfkill) if (!rfkill->ops->poll) return; + rfkill->polling_paused = false; + + if (rfkill->suspended) + return; + queue_delayed_work(system_power_efficient_wq, &rfkill->poll_work, 0); } @@ -807,7 +815,8 @@ static int rfkill_suspend(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); - rfkill_pause_polling(rfkill); + rfkill->suspended = true; + cancel_delayed_work_sync(&rfkill->poll_work); return 0; } @@ -817,12 +826,16 @@ static int rfkill_resume(struct device *dev) struct rfkill *rfkill = to_rfkill(dev); bool cur; + rfkill->suspended = false; + if (!rfkill->persistent) { cur = !!(rfkill->state & RFKILL_BLOCK_SW); rfkill_set_block(rfkill, cur); } - rfkill_resume_polling(rfkill); + if (rfkill->ops->poll && !rfkill->polling_paused) + queue_delayed_work(system_power_efficient_wq, + &rfkill->poll_work, 0); return 0; } -- cgit From 573a2b51acb48e745094cf3cb942cf6a55d2bf30 Mon Sep 17 00:00:00 2001 From: Ola Olsson Date: Sun, 10 Jan 2016 18:41:42 +0100 Subject: cfg80211: Fix some linguistics in Kconfig Signed-off-by: Ola Olsson Signed-off-by: Johannes Berg --- net/wireless/Kconfig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index ec3bf30dd526..6c606120abfe 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -50,8 +50,8 @@ config CFG80211_DEVELOPER_WARNINGS default n help This option enables some additional warnings that help - cfg80211 developers and driver developers, but that can - trigger due to races with userspace. + cfg80211 developers and driver developers, but beware that + they can also trigger due to races with userspace. For example, when a driver reports that it was disconnected from the AP, but the user disconnects manually at the same @@ -110,7 +110,7 @@ config CFG80211_REG_RELAX_NO_IR interface which associated to an AP which userspace assumes or confirms to be an authorized master, i.e., with radar detection support and DFS capabilities. However, note that in order to not create daisy chain - scenarios, this relaxation is not allowed in cases that the BSS client + scenarios, this relaxation is not allowed in cases where the BSS client is associated to P2P GO and in addition the P2P GO instantiated on a channel due to this relaxation should not allow connection from non P2P clients. @@ -135,7 +135,7 @@ config CFG80211_DEBUGFS depends on CFG80211 depends on DEBUG_FS ---help--- - You can enable this if you want to debugfs entries for cfg80211. + You can enable this if you want debugfs entries for cfg80211. If unsure, say N. @@ -146,7 +146,7 @@ config CFG80211_INTERNAL_REGDB ---help--- This option generates an internal data structure representing the wireless regulatory rules described in net/wireless/db.txt - and includes code to query that database. This is an alternative + and includes code to query that database. This is an alternative to using CRDA for defining regulatory rules for the kernel. Using this option requires some parsing of the db.txt at build time, @@ -159,7 +159,7 @@ config CFG80211_INTERNAL_REGDB http://wireless.kernel.org/en/developers/Regulatory - Most distributions have a CRDA package. So if unsure, say N. + Most distributions have a CRDA package. So if unsure, say N. config CFG80211_CRDA_SUPPORT bool "support CRDA" if CFG80211_INTERNAL_REGDB -- cgit From f3e7fae248224b9441098e3c344b48b1cd1d9eca Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Tue, 19 Jan 2016 10:42:37 -0500 Subject: rfkill: use variable instead of duplicating the expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFKILL_BLOCK_SW value have just been saved to prev, no need to check it again in the if expression. This makes code a little bit easier to read. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 166439995f34..061ed37c07cf 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -287,7 +287,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked) spin_lock_irqsave(&rfkill->lock, flags); prev = rfkill->state & RFKILL_BLOCK_SW; - if (rfkill->state & RFKILL_BLOCK_SW) + if (prev) rfkill->state |= RFKILL_BLOCK_SW_PREV; else rfkill->state &= ~RFKILL_BLOCK_SW_PREV; -- cgit From 1926e260d8d377b95b57a49eefeb54ac42919f53 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Tue, 19 Jan 2016 10:42:38 -0500 Subject: rfkill: remove/inline __rfkill_set_hw_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __rfkill_set_hw_state() is only one used in rfkill_set_hw_state(), and none of them are long or complicated, so merging the two makes the code easier to read. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 061ed37c07cf..076590e92dde 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -235,29 +235,6 @@ static void rfkill_event(struct rfkill *rfkill) rfkill_send_events(rfkill, RFKILL_OP_CHANGE); } -static bool __rfkill_set_hw_state(struct rfkill *rfkill, - bool blocked, bool *change) -{ - unsigned long flags; - bool prev, any; - - BUG_ON(!rfkill); - - spin_lock_irqsave(&rfkill->lock, flags); - prev = !!(rfkill->state & RFKILL_BLOCK_HW); - if (blocked) - rfkill->state |= RFKILL_BLOCK_HW; - else - rfkill->state &= ~RFKILL_BLOCK_HW; - *change = prev != blocked; - any = !!(rfkill->state & RFKILL_BLOCK_ANY); - spin_unlock_irqrestore(&rfkill->lock, flags); - - rfkill_led_trigger_event(rfkill); - - return any; -} - /** * rfkill_set_block - wrapper for set_block method * @@ -482,14 +459,26 @@ bool rfkill_get_global_sw_state(const enum rfkill_type type) bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) { - bool ret, change; + unsigned long flags; + bool ret, prev; + + BUG_ON(!rfkill); + + spin_lock_irqsave(&rfkill->lock, flags); + prev = !!(rfkill->state & RFKILL_BLOCK_HW); + if (blocked) + rfkill->state |= RFKILL_BLOCK_HW; + else + rfkill->state &= ~RFKILL_BLOCK_HW; + ret = !!(rfkill->state & RFKILL_BLOCK_ANY); + spin_unlock_irqrestore(&rfkill->lock, flags); - ret = __rfkill_set_hw_state(rfkill, blocked, &change); + rfkill_led_trigger_event(rfkill); if (!rfkill->registered) return ret; - if (change) + if (prev != blocked) schedule_work(&rfkill->uevent_work); return ret; -- cgit From e2a35e89291d70d1c4668b3216f84ec740d36be3 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Tue, 19 Jan 2016 10:42:39 -0500 Subject: rfkill: Remove obsolete "claim" sysfs interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was scheduled to be removed in 2012 by: commit 69c86373c6ea1149aa559e6088362d58d8ec8835 Author: florian@mickler.org Date: Wed Feb 24 12:05:16 2010 +0100 Document the rfkill sysfs ABI This moves sysfs ABI info from Documentation/rfkill.txt to the ABI subfolder and reformats it. This also schedules the deprecated sysfs parts to be removed in 2012 (claim file) and 2014 (state file). Signed-off-by: Florian Mickler Signed-off-by: John W. Linville Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- Documentation/ABI/obsolete/sysfs-class-rfkill | 9 --------- Documentation/ABI/removed/sysfs-class-rfkill | 13 +++++++++++++ net/rfkill/core.c | 11 +---------- 3 files changed, 14 insertions(+), 19 deletions(-) create mode 100644 Documentation/ABI/removed/sysfs-class-rfkill diff --git a/Documentation/ABI/obsolete/sysfs-class-rfkill b/Documentation/ABI/obsolete/sysfs-class-rfkill index ff60ad9eca4c..e736d145085f 100644 --- a/Documentation/ABI/obsolete/sysfs-class-rfkill +++ b/Documentation/ABI/obsolete/sysfs-class-rfkill @@ -18,12 +18,3 @@ Values: A numeric value. 2: RFKILL_STATE_HARD_BLOCKED transmitter is forced off by something outside of the driver's control. - -What: /sys/class/rfkill/rfkill[0-9]+/claim -Date: 09-Jul-2007 -KernelVersion v2.6.22 -Contact: linux-wireless@vger.kernel.org -Description: This file is deprecated because there no longer is a way to - claim just control over a single rfkill instance. - This file is scheduled to be removed in 2012. -Values: 0: Kernel handles events diff --git a/Documentation/ABI/removed/sysfs-class-rfkill b/Documentation/ABI/removed/sysfs-class-rfkill new file mode 100644 index 000000000000..3ce6231f20b2 --- /dev/null +++ b/Documentation/ABI/removed/sysfs-class-rfkill @@ -0,0 +1,13 @@ +rfkill - radio frequency (RF) connector kill switch support + +For details to this subsystem look at Documentation/rfkill.txt. + +What: /sys/class/rfkill/rfkill[0-9]+/claim +Date: 09-Jul-2007 +KernelVersion v2.6.22 +Contact: linux-wireless@vger.kernel.org +Description: This file was deprecated because there no longer was a way to + claim just control over a single rfkill instance. + This file was scheduled to be removed in 2012, and was removed + in 2016. +Values: 0: Kernel handles events diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 076590e92dde..a805831d5d9b 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -311,8 +311,7 @@ static atomic_t rfkill_input_disabled = ATOMIC_INIT(0); * @blocked: the new state * * This function sets the state of all switches of given type, - * unless a specific switch is claimed by userspace (in which case, - * that switch is left alone) or suspended. + * unless a specific switch is suspended. * * Caller must have acquired rfkill_global_mutex. */ @@ -721,20 +720,12 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(state); -static ssize_t claim_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", 0); -} -static DEVICE_ATTR_RO(claim); - static struct attribute *rfkill_dev_attrs[] = { &dev_attr_name.attr, &dev_attr_type.attr, &dev_attr_index.attr, &dev_attr_persistent.attr, &dev_attr_state.attr, - &dev_attr_claim.attr, &dev_attr_soft.attr, &dev_attr_hard.attr, NULL, -- cgit From d4634e8dea13ccc969dd3f33dab3873cfdf3bc51 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Tue, 19 Jan 2016 10:42:42 -0500 Subject: rfkill: Update userspace API documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a note to userspace on the effect of RFKILL_OP_CHANGE_ALL also updating the default state for hotplugged devices. Signed-off-by: João Paulo Rechi Vita [reword a bit] Signed-off-by: Johannes Berg --- include/uapi/linux/rfkill.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/rfkill.h b/include/uapi/linux/rfkill.h index 058757f7a733..2e00dcebebd0 100644 --- a/include/uapi/linux/rfkill.h +++ b/include/uapi/linux/rfkill.h @@ -59,6 +59,8 @@ enum rfkill_type { * @RFKILL_OP_DEL: a device was removed * @RFKILL_OP_CHANGE: a device's state changed -- userspace changes one device * @RFKILL_OP_CHANGE_ALL: userspace changes all devices (of a type, or all) + * into a state, also updating the default state used for devices that + * are hot-plugged later. */ enum rfkill_operation { RFKILL_OP_ADD = 0, -- cgit From b8631c003307bc7abf0180d950c42d9032ebda70 Mon Sep 17 00:00:00 2001 From: Sunil Shahu Date: Thu, 21 Jan 2016 11:06:25 +0530 Subject: mac80211: mesh_plink: remove redundant sta_info check Remove unnecessory "if" statement and club it with previos "if" block. Signed-off-by: Sunil Shahu Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index bd3d55eb21d4..a07e93c21c9e 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -976,6 +976,10 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); goto out; } + + /* new matching peer */ + event = OPN_ACPT; + goto out; } else { if (!test_sta_flag(sta, WLAN_STA_AUTH)) { mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); @@ -985,12 +989,6 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, goto out; } - /* new matching peer */ - if (!sta) { - event = OPN_ACPT; - goto out; - } - switch (ftype) { case WLAN_SP_MESH_PEERING_OPEN: if (!matches_local) -- cgit From 2a58d42c1e018ad514d4e23fd33fb2ded95d3ee6 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Thu, 21 Jan 2016 14:23:07 +0100 Subject: mac80211: fix txq queue related crashes The driver can access the queue simultanously while mac80211 tears down the interface. Without spinlock protection this could lead to corrupting sk_buff_head and subsequently to an invalid pointer dereference. Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation") Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 33ae3c81bfc5..0451f120746e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -977,7 +977,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (sdata->vif.txq) { struct txq_info *txqi = to_txq_info(sdata->vif.txq); + spin_lock_bh(&txqi->queue.lock); ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + spin_unlock_bh(&txqi->queue.lock); + atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); } -- cgit From cf44012810ccdd8fd947518e965cb04b7b8498be Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 25 Jan 2016 14:43:24 +0100 Subject: mac80211: fix unnecessary frame drops in mesh fwding The ieee80211_queue_stopped() expects hw queue number but it was given raw WMM AC number instead. This could cause frame drops and problems with traffic in some cases - most notably if driver doesn't map AC numbers to queue numbers 1:1 and uses ieee80211_stop_queues() and ieee80211_wake_queue() only without ever calling ieee80211_wake_queues(). On ath10k it was possible to hit this problem in the following case: 1. wlan0 uses queue 0 (ath10k maps queues per vif) 2. offchannel uses queue 15 3. queues 1-14 are unused 4. ieee80211_stop_queues() 5. ieee80211_wake_queue(q=0) 6. ieee80211_wake_queue(q=15) (other queues are not woken up because both driver and mac80211 know other queues are unused) 7. ieee80211_rx_h_mesh_fwding() 8. ieee80211_select_queue_80211() returns 2 9. ieee80211_queue_stopped(q=2) returns true 10. frame is dropped (oops!) Fixes: d3c1597b8d1b ("mac80211: fix forwarded mesh frame queue mapping") Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index ae993edfdecf..664e8861edbe 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2235,7 +2235,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - u16 q, hdrlen; + u16 ac, q, hdrlen; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -2304,7 +2304,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ether_addr_equal(sdata->vif.addr, hdr->addr3)) return RX_CONTINUE; - q = ieee80211_select_queue_80211(sdata, skb, hdr); + ac = ieee80211_select_queue_80211(sdata, skb, hdr); + q = sdata->vif.hw_queue[ac]; if (ieee80211_queue_stopped(&local->hw, q)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); return RX_DROP_MONITOR; -- cgit From f4a0f0c5264e72d9279fbf9cf48a061526e8f788 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 25 Jan 2016 15:46:34 +0200 Subject: mac80211: add RX_FLAG_MACTIME_PLCP_START The timestamp given by iwlwifi is at the beginning of the frame over the air, at (or during) the SYNC field. Allow such timestamps to be given to mac80211, at least (for now) for frames with non-HT/VHT preambles. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 3 +++ net/mac80211/ieee80211_i.h | 8 +++++++- net/mac80211/util.c | 14 +++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 566df20dc957..31337f81ec03 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1010,6 +1010,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_MACTIME_END: The timestamp passed in the RX status (@mactime * field) is valid and contains the time the last symbol of the MPDU * (including FCS) was received. + * @RX_FLAG_MACTIME_PLCP_START: The timestamp passed in the RX status (@mactime + * field) is valid and contains the time the SYNC preamble was received. * @RX_FLAG_SHORTPRE: Short preamble was used for this frame * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_VHT: VHT MCS was used and rate_index is MCS index @@ -1058,6 +1060,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = BIT(0), RX_FLAG_DECRYPTED = BIT(1), + RX_FLAG_MACTIME_PLCP_START = BIT(2), RX_FLAG_MMIC_STRIPPED = BIT(3), RX_FLAG_IV_STRIPPED = BIT(4), RX_FLAG_FAILED_FCS_CRC = BIT(5), diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9934447a8b9a..a29f61dc9c06 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1466,7 +1466,13 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) { WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START && status->flag & RX_FLAG_MACTIME_END); - return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END); + if (status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END)) + return true; + /* can't handle HT/VHT preamble yet */ + if (status->flag & RX_FLAG_MACTIME_PLCP_START && + !(status->flag & (RX_FLAG_HT | RX_FLAG_VHT))) + return true; + return false; } u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7d0479e31674..fb90d9c5df59 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4,7 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 Intel Deutschland GmbH + * Copyright (C) 2015-2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -2671,6 +2671,18 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, sband = local->hw.wiphy->bands[status->band]; bitrate = sband->bitrates[status->rate_idx].bitrate; ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); + + if (status->flag & RX_FLAG_MACTIME_PLCP_START) { + /* TODO: handle HT/VHT preambles */ + if (status->band == IEEE80211_BAND_5GHZ) { + ts += 20 << shift; + mpdu_offset += 2; + } else if (status->flag & RX_FLAG_SHORTPRE) { + ts += 96; + } else { + ts += 192; + } + } } rate = cfg80211_calculate_bitrate(&ri); -- cgit From d321cd014e51baab475efbdec468255b9e0ec822 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 25 Jan 2016 15:46:35 +0200 Subject: mac80211: fix ibss scan parameters When joining IBSS a full scan should be initiated in order to search for existing cell, unless the fixed_channel parameter was set. A default channel to create the IBSS on if no cell was found is provided as well. However - a scan is initiated only on the default channel provided regardless of whether ifibss->fixed_channel is set or not, with the obvious result of the cell not joining existing IBSS cell that is on another channel. Fixes: 76bed0f43b27 ("mac80211: IBSS fix scan request") Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index e5e8a317b865..9b983788ee51 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -7,6 +7,7 @@ * Copyright 2007, Michael Wu * Copyright 2009, Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -1483,14 +1484,21 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); - num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy, - &ifibss->chandef, - channels, - ARRAY_SIZE(channels)); scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); - ieee80211_request_ibss_scan(sdata, ifibss->ssid, - ifibss->ssid_len, channels, num, - scan_width); + + if (ifibss->fixed_channel) { + num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy, + &ifibss->chandef, + channels, + ARRAY_SIZE(channels)); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, channels, + num, scan_width); + } else { + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, NULL, + 0, scan_width); + } } else { int interval = IEEE80211_SCAN_INTERVAL; -- cgit From 89f774e6c41efdcede9d4b37867af5aabcc835d5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 25 Jan 2016 15:46:36 +0200 Subject: mac80211: always print a message when disconnecting Make sure there's at least a debug message whenever the connection to the AP is terminated. Also change one message from wiphy_debug() to the common mlme_dbg(). Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e6d198b981ab..857089de475f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3544,6 +3544,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, elems.ht_cap_elem, elems.ht_operation, elems.vht_operation, bssid, &changed)) { mutex_unlock(&local->sta_mtx); + sdata_info(sdata, + "failed to follow AP %pM bandwidth change, disconnect\n", + bssid); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, true, deauth_buf); @@ -3919,11 +3922,9 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) * We actually lost the connection ... or did we? * Let's make sure! */ - wiphy_debug(local->hw.wiphy, - "%s: No probe response from AP %pM" - " after %dms, disconnecting.\n", - sdata->name, - bssid, probe_wait_ms); + mlme_dbg(sdata, + "No probe response from AP %pM after %dms, disconnecting.\n", + bssid, probe_wait_ms); ieee80211_sta_connection_lost(sdata, bssid, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); @@ -4511,6 +4512,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + sdata_info(sdata, + "disconnect from AP %pM for new auth to %pM\n", + ifmgd->associated->bssid, req->bss->bssid); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_UNSPECIFIED, false, frame_buf); @@ -4579,6 +4583,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + sdata_info(sdata, + "disconnect from AP %pM for new assoc to %pM\n", + ifmgd->associated->bssid, req->bss->bssid); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_UNSPECIFIED, false, frame_buf); -- cgit From 0ef049dc1167fe834d0ad5d63f89eddc5c70f6e4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 Jan 2016 23:05:31 +0100 Subject: mac80211: avoid excessive stack usage in sta_info When CONFIG_OPTIMIZE_INLINING is set, the sta_info_insert_finish function consumes more stack than normally, exceeding the 1024 byte limit on ARM: net/mac80211/sta_info.c: In function 'sta_info_insert_finish': net/mac80211/sta_info.c:561:1: error: the frame size of 1080 bytes is larger than 1024 bytes [-Werror=frame-larger-than=] It turns out that there are two functions that put a 'struct station_info' on the stack: __sta_info_destroy_part2 and sta_info_insert_finish, and this structure alone requires up to 792 bytes. Hoping that both are called rarely enough, this replaces the on-stack structure with a dynamic allocation, which unfortunately requires some suboptimal error handling for out-of-memory. The __sta_info_destroy_part2 function is actually affected by the stack usage twice because it calls cfg80211_del_sta_sinfo(), which has another instance of struct station_info on its stack. Signed-off-by: Arnd Bergmann Fixes: 98b6218388e3 ("mac80211/cfg80211: add station events") Fixes: 6f7a8d26e266 ("mac80211: send statistics with delete station event") Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 7e007cf12cb2..b28e7a220d56 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -498,11 +498,17 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - struct station_info sinfo; + struct station_info *sinfo; int err = 0; lockdep_assert_held(&local->sta_mtx); + sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL); + if (!sinfo) { + err = -ENOMEM; + goto out_err; + } + /* check if STA exists already */ if (sta_info_get_bss(sdata, sta->sta.addr)) { err = -EEXIST; @@ -533,10 +539,9 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = 0; - sinfo.generation = local->sta_generation; - cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); + sinfo->generation = local->sta_generation; + cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL); + kfree(sinfo); sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr); @@ -897,7 +902,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - struct station_info sinfo = {}; + struct station_info *sinfo; int ret; /* @@ -935,8 +940,11 @@ static void __sta_info_destroy_part2(struct sta_info *sta) sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); - sta_set_sinfo(sta, &sinfo); - cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (sinfo) + sta_set_sinfo(sta, sinfo); + cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL); + kfree(sinfo); rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); -- cgit From 3f73fe9fd8703123faf663908923ca9e62cb984e Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sat, 30 Jan 2016 13:17:03 -0500 Subject: mac80211: mesh: drop constant field mean_chain_len The mean_chain_len field in struct mesh_table is copied whenever a new mesh table is allocated, but only ever has the value 2 and is never otherwise updated, so just remove it and use the related define instead. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh.h | 3 --- net/mac80211/mesh_pathtbl.c | 9 +++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index a1596344c3ba..d941f3a73a4f 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -137,8 +137,6 @@ struct mesh_path { * @copy_node: function to copy nodes of the table * @size_order: determines size of the table, there will be 2^size_order hash * buckets - * @mean_chain_len: maximum average length for the hash buckets' list, if it is - * reached, the table will grow * @known_gates: list of known mesh gates and their mpaths by the station. The * gate's mpath may or may not be resolved and active. * @@ -154,7 +152,6 @@ struct mesh_table { void (*free_node) (struct hlist_node *p, bool free_leafs); int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); int size_order; - int mean_chain_len; struct hlist_head *known_gates; spinlock_t gates_lock; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index dadf8dc6f1cf..c95bdb8bb452 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -160,11 +160,10 @@ static int mesh_table_grow(struct mesh_table *oldtbl, int i; if (atomic_read(&oldtbl->entries) - < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) + < MEAN_CHAIN_LEN * (oldtbl->hash_mask + 1)) return -EAGAIN; newtbl->free_node = oldtbl->free_node; - newtbl->mean_chain_len = oldtbl->mean_chain_len; newtbl->copy_node = oldtbl->copy_node; newtbl->known_gates = oldtbl->known_gates; atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); @@ -585,7 +584,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, hlist_add_head_rcu(&new_node->list, bucket); if (atomic_inc_return(&tbl->entries) >= - tbl->mean_chain_len * (tbl->hash_mask + 1)) + MEAN_CHAIN_LEN * (tbl->hash_mask + 1)) grow = 1; mesh_paths_generation++; @@ -714,7 +713,7 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, hlist_add_head_rcu(&new_node->list, bucket); if (atomic_inc_return(&tbl->entries) >= - tbl->mean_chain_len * (tbl->hash_mask + 1)) + MEAN_CHAIN_LEN * (tbl->hash_mask + 1)) grow = 1; spin_unlock(&tbl->hashwlock[hash_idx]); @@ -1076,7 +1075,6 @@ int mesh_pathtbl_init(void) return -ENOMEM; tbl_path->free_node = &mesh_path_node_free; tbl_path->copy_node = &mesh_path_node_copy; - tbl_path->mean_chain_len = MEAN_CHAIN_LEN; tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); if (!tbl_path->known_gates) { ret = -ENOMEM; @@ -1092,7 +1090,6 @@ int mesh_pathtbl_init(void) } tbl_mpp->free_node = &mesh_path_node_free; tbl_mpp->copy_node = &mesh_path_node_copy; - tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); if (!tbl_mpp->known_gates) { ret = -ENOMEM; -- cgit From dfdfc2beb0dd7e3a067d2eeacb4623cb48e77658 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 26 Jan 2016 17:11:13 +0100 Subject: mac80211: Parse legacy and HT rate in injected frames Drivers/devices without their own rate control algorithm can get the information what rates they should use from either the radiotap header of injected frames or from the rate control algorithm. But the parsing of the legacy rate information from the radiotap header was removed in commit e6a9854b05c1 ("mac80211/drivers: rewrite the rate control API"). The removal of this feature heavily reduced the usefulness of frame injection when wanting to simulate specific transmission behavior. Having rate parsing together with MCS rates and retry support allows a fine grained selection of the tx behavior of injected frames for these kind of tests. Signed-off-by: Sven Eckelmann Cc: Simon Wunderlich Signed-off-by: Johannes Berg --- Documentation/networking/mac80211-injection.txt | 17 ++++++ include/net/mac80211.h | 2 + net/mac80211/tx.c | 72 ++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/mac80211-injection.txt b/Documentation/networking/mac80211-injection.txt index 3a930072b161..ec8f934c2eb2 100644 --- a/Documentation/networking/mac80211-injection.txt +++ b/Documentation/networking/mac80211-injection.txt @@ -28,6 +28,23 @@ radiotap headers and used to control injection: IEEE80211_RADIOTAP_F_TX_NOACK: frame should be sent without waiting for an ACK even if it is a unicast frame + * IEEE80211_RADIOTAP_RATE + + legacy rate for the transmission (only for devices without own rate control) + + * IEEE80211_RADIOTAP_MCS + + HT rate for the transmission (only for devices without own rate control). + Also some flags are parsed + + IEEE80211_TX_RC_SHORT_GI: use short guard interval + IEEE80211_TX_RC_40_MHZ_WIDTH: send in HT40 mode + + * IEEE80211_RADIOTAP_DATA_RETRIES + + number of retries when either IEEE80211_RADIOTAP_RATE or + IEEE80211_RADIOTAP_MCS was used + The injection code can also skip all other currently defined radiotap fields facilitating replay of captured radiotap headers directly. diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 31337f81ec03..dbcd69a6bfda 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -708,12 +708,14 @@ enum mac80211_tx_info_flags { * protocol frame (e.g. EAP) * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll * frame (PS-Poll or uAPSD). + * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information * * These flags are used in tx_info->control.flags. */ enum mac80211_tx_control_flags { IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0), IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1), + IEEE80211_TX_CTRL_RATE_INJECT = BIT(2), }; /* diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3311ce0f3d6c..723cd7aa8953 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) info->control.short_preamble = txrc.short_preamble; + /* don't ask rate control when rate already injected via radiotap */ + if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT) + return TX_CONTINUE; + if (tx->sta) assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); @@ -1665,15 +1669,24 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, ieee80211_tx(sdata, sta, skb, false); } -static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) +static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local, + struct sk_buff *skb) { struct ieee80211_radiotap_iterator iterator; struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[info->band]; int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); u16 txflags; + u16 rate = 0; + bool rate_found = false; + u8 rate_retries = 0; + u16 rate_flags = 0; + u8 mcs_known, mcs_flags; + int i; info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_DONTFRAG; @@ -1724,6 +1737,35 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) info->flags |= IEEE80211_TX_CTL_NO_ACK; break; + case IEEE80211_RADIOTAP_RATE: + rate = *iterator.this_arg; + rate_flags = 0; + rate_found = true; + break; + + case IEEE80211_RADIOTAP_DATA_RETRIES: + rate_retries = *iterator.this_arg; + break; + + case IEEE80211_RADIOTAP_MCS: + mcs_known = iterator.this_arg[0]; + mcs_flags = iterator.this_arg[1]; + if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS)) + break; + + rate_found = true; + rate = iterator.this_arg[2]; + rate_flags = IEEE80211_TX_RC_MCS; + + if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI && + mcs_flags & IEEE80211_RADIOTAP_MCS_SGI) + rate_flags |= IEEE80211_TX_RC_SHORT_GI; + + if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW && + mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40) + rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + /* * Please update the file * Documentation/networking/mac80211-injection.txt @@ -1738,6 +1780,32 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ return false; + if (rate_found) { + info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT; + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + info->control.rates[i].idx = -1; + info->control.rates[i].flags = 0; + info->control.rates[i].count = 0; + } + + if (rate_flags & IEEE80211_TX_RC_MCS) { + info->control.rates[0].idx = rate; + } else { + for (i = 0; i < sband->n_bitrates; i++) { + if (rate * 5 != sband->bitrates[i].bitrate) + continue; + + info->control.rates[0].idx = i; + break; + } + } + + info->control.rates[0].flags = rate_flags; + info->control.rates[0].count = min_t(u8, rate_retries + 1, + local->hw.max_rate_tries); + } + /* * remove the radiotap header * iterator->_max_length was sanity-checked against @@ -1819,7 +1887,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, IEEE80211_TX_CTL_INJECTED; /* process and remove the injection radiotap header */ - if (!ieee80211_parse_tx_radiotap(skb)) + if (!ieee80211_parse_tx_radiotap(local, skb)) goto fail; rcu_read_lock(); -- cgit From f2ac7e301ae6397669ff3f79e691942a9b5d2f39 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 27 Jan 2016 15:26:12 +0100 Subject: mac80211: expose txq queue depth and size to drivers This will allow drivers to make more educated decisions whether to defer transmission or not. Relying on wake_tx_queue() call count implicitly was not possible because it could be called without queued frame count actually changing on software tx aggregation start/stop code paths. It was also not possible to know how long byte-wise queue was without dequeueing. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/mac80211.h | 15 +++++++++++++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 1 + net/mac80211/sta_info.c | 1 + net/mac80211/tx.c | 8 +++++++- net/mac80211/util.c | 14 ++++++++++++++ 6 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dbcd69a6bfda..fd35fc4d7127 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5596,4 +5596,19 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid); */ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, struct ieee80211_txq *txq); + +/** + * ieee80211_txq_get_depth - get pending frame/byte count of given txq + * + * The values are not guaranteed to be coherent with regard to each other, i.e. + * txq state can change half-way of this function and the caller may end up + * with "new" frame_cnt and "old" byte_cnt or vice-versa. + * + * @txq: pointer obtained from station or virtual interface + * @frame_cnt: pointer to store frame count + * @byte_cnt: pointer to store byte count + */ +void ieee80211_txq_get_depth(struct ieee80211_txq *txq, + unsigned long *frame_cnt, + unsigned long *byte_cnt); #endif /* MAC80211_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a29f61dc9c06..a96f8c0461f6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -804,6 +804,7 @@ enum txq_info_flags { struct txq_info { struct sk_buff_head queue; unsigned long flags; + unsigned long byte_cnt; /* keep last! */ struct ieee80211_txq txq; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0451f120746e..453b4e741780 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -979,6 +979,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, spin_lock_bh(&txqi->queue.lock); ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + txqi->byte_cnt = 0; spin_unlock_bh(&txqi->queue.lock); atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index b28e7a220d56..5894c0a1c01f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -116,6 +116,7 @@ static void __cleanup_single_sta(struct sta_info *sta) ieee80211_purge_tx_queue(&local->hw, &txqi->queue); atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]); + txqi->byte_cnt = 0; } } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 723cd7aa8953..a5aa275d0434 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1270,7 +1270,11 @@ static void ieee80211_drv_tx(struct ieee80211_local *local, if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending) netif_stop_subqueue(sdata->dev, ac); - skb_queue_tail(&txqi->queue, skb); + spin_lock_bh(&txqi->queue.lock); + txqi->byte_cnt += skb->len; + __skb_queue_tail(&txqi->queue, skb); + spin_unlock_bh(&txqi->queue.lock); + drv_wake_tx_queue(local, txqi); return; @@ -1298,6 +1302,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, if (!skb) goto out; + txqi->byte_cnt -= skb->len; + atomic_dec(&sdata->txqs_len[ac]); if (__netif_subqueue_stopped(sdata->dev, ac)) ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index fb90d9c5df59..091f3dd62ad1 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3368,3 +3368,17 @@ void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, txqi->txq.ac = IEEE80211_AC_BE; } } + +void ieee80211_txq_get_depth(struct ieee80211_txq *txq, + unsigned long *frame_cnt, + unsigned long *byte_cnt) +{ + struct txq_info *txqi = to_txq_info(txq); + + if (frame_cnt) + *frame_cnt = txqi->queue.qlen; + + if (byte_cnt) + *byte_cnt = txqi->byte_cnt; +} +EXPORT_SYMBOL(ieee80211_txq_get_depth); -- cgit From fb4ea054857ec719dba84c3b5a7c4ee50d42bd5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Jan 2016 16:19:24 +0200 Subject: mac80211: change ieee80211_rx_reorder_ready() arguments Clean up ieee80211_rx_reorder_ready() callers by passing the RX TID struct and the index, instead of the frames list. This will make it more extensible as well. While at it, move the inline to rx.c as it's only used there. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 14 -------------- net/mac80211/rx.c | 33 ++++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a96f8c0461f6..a49c10361f1c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1838,20 +1838,6 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len, ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); } -static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames) -{ - struct sk_buff *tail = skb_peek_tail(frames); - struct ieee80211_rx_status *status; - - if (!tail) - return false; - - status = IEEE80211_SKB_RXCB(tail); - if (status->flag & RX_FLAG_AMSDU_MORE) - return false; - - return true; -} extern const int ieee802_1d_to_ac[8]; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 664e8861edbe..1153871b570f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4,6 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -798,6 +799,23 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) return RX_CONTINUE; } +static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx, + int index) +{ + struct sk_buff_head *frames = &tid_agg_rx->reorder_buf[index]; + struct sk_buff *tail = skb_peek_tail(frames); + struct ieee80211_rx_status *status; + + if (!tail) + return false; + + status = IEEE80211_SKB_RXCB(tail); + if (status->flag & RX_FLAG_AMSDU_MORE) + return false; + + return true; +} + static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, int index, @@ -812,7 +830,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, if (skb_queue_empty(skb_list)) goto no_frame; - if (!ieee80211_rx_reorder_ready(skb_list)) { + if (!ieee80211_rx_reorder_ready(tid_agg_rx, index)) { __skb_queue_purge(skb_list); goto no_frame; } @@ -866,7 +884,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, /* release the buffer until next missing frame */ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; - if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) && + if (!ieee80211_rx_reorder_ready(tid_agg_rx, index) && tid_agg_rx->stored_mpdu_num) { /* * No buffers ready to be released, but check whether any @@ -875,8 +893,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, int skipped = 1; for (j = (index + 1) % tid_agg_rx->buf_size; j != index; j = (j + 1) % tid_agg_rx->buf_size) { - if (!ieee80211_rx_reorder_ready( - &tid_agg_rx->reorder_buf[j])) { + if (!ieee80211_rx_reorder_ready(tid_agg_rx, j)) { skipped++; continue; } @@ -903,8 +920,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, skipped) & IEEE80211_SN_MASK; skipped = 0; } - } else while (ieee80211_rx_reorder_ready( - &tid_agg_rx->reorder_buf[index])) { + } else while (ieee80211_rx_reorder_ready(tid_agg_rx, index)) { ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, frames); index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; @@ -915,8 +931,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, for (; j != (index - 1) % tid_agg_rx->buf_size; j = (j + 1) % tid_agg_rx->buf_size) { - if (ieee80211_rx_reorder_ready( - &tid_agg_rx->reorder_buf[j])) + if (ieee80211_rx_reorder_ready(tid_agg_rx, j)) break; } @@ -987,7 +1002,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata index = mpdu_seq_num % tid_agg_rx->buf_size; /* check if we already stored this frame */ - if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) { + if (ieee80211_rx_reorder_ready(tid_agg_rx, index)) { dev_kfree_skb(skb); goto out; } -- cgit From 06470f7468c8b6c95e72ebda803a61a99f4ee446 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 28 Jan 2016 16:19:25 +0200 Subject: mac80211: add API to allow filtering frames in BA sessions If any frames are dropped that are part of a BA session, the reorder buffer will "indefinitely" (until the timeout) wait for them to come in (or a BAR moving the window) and won't release frames after them. This means it isn't possible to filter frames within a BA session in firmware. Introduce an API function that allows such filtering. Calling this function will move the BA window forward to the new SSN, and allows marking frames after the SSN as having been filtered, so any future reordering activity will release frames while skipping the holes. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 20 +++++++++++- net/mac80211/agg-rx.c | 1 + net/mac80211/rx.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/sta_info.h | 3 ++ 4 files changed, 107 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index fd35fc4d7127..57147749ae42 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 Intel Deutschland GmbH + * Copyright (C) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -5193,6 +5193,24 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw); void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, const u8 *addr); +/** + * ieee80211_mark_rx_ba_filtered_frames - move RX BA window and mark filtered + * @pubsta: station struct + * @tid: the session's TID + * @ssn: starting sequence number of the bitmap, all frames before this are + * assumed to be out of the window after the call + * @filtered: bitmap of filtered frames, BIT(0) is the @ssn entry etc. + * @received_mpdus: number of received mpdus in firmware + * + * This function moves the BA window and releases all frames before @ssn, and + * marks frames marked in the bitmap as having been filtered. Afterwards, it + * checks if any frames in the window starting from @ssn can now be released + * (in case they were only waiting for frames that were filtered.) + */ +void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid, + u16 ssn, u64 filtered, + u16 received_mpdus); + /** * ieee80211_send_bar - send a BlockAckReq frame * diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 2ab54791281d..1b8a5caa221e 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -376,6 +376,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, tid_agg_rx->timeout = timeout; tid_agg_rx->stored_mpdu_num = 0; tid_agg_rx->auto_seq = auto_seq; + tid_agg_rx->reorder_buf_filtered = 0; status = WLAN_STATUS_SUCCESS; /* activate it for RX */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1153871b570f..9fb7074f0280 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -806,6 +807,9 @@ static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx, struct sk_buff *tail = skb_peek_tail(frames); struct ieee80211_rx_status *status; + if (tid_agg_rx->reorder_buf_filtered & BIT_ULL(index)) + return true; + if (!tail) return false; @@ -844,6 +848,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, } no_frame: + tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index); tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num); } @@ -3300,6 +3305,85 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) ieee80211_rx_handlers(&rx, &frames); } +void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid, + u16 ssn, u64 filtered, + u16 received_mpdus) +{ + struct sta_info *sta; + struct tid_ampdu_rx *tid_agg_rx; + struct sk_buff_head frames; + struct ieee80211_rx_data rx = { + /* This is OK -- must be QoS data frame */ + .security_idx = tid, + .seqno_idx = tid, + }; + int i, diff; + + if (WARN_ON(!pubsta || tid >= IEEE80211_NUM_TIDS)) + return; + + __skb_queue_head_init(&frames); + + sta = container_of(pubsta, struct sta_info, sta); + + rx.sta = sta; + rx.sdata = sta->sdata; + rx.local = sta->local; + + rcu_read_lock(); + tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); + if (!tid_agg_rx) + goto out; + + spin_lock_bh(&tid_agg_rx->reorder_lock); + + if (received_mpdus >= IEEE80211_SN_MODULO >> 1) { + int release; + + /* release all frames in the reorder buffer */ + release = (tid_agg_rx->head_seq_num + tid_agg_rx->buf_size) % + IEEE80211_SN_MODULO; + ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx, + release, &frames); + /* update ssn to match received ssn */ + tid_agg_rx->head_seq_num = ssn; + } else { + ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx, ssn, + &frames); + } + + /* handle the case that received ssn is behind the mac ssn. + * it can be tid_agg_rx->buf_size behind and still be valid */ + diff = (tid_agg_rx->head_seq_num - ssn) & IEEE80211_SN_MASK; + if (diff >= tid_agg_rx->buf_size) { + tid_agg_rx->reorder_buf_filtered = 0; + goto release; + } + filtered = filtered >> diff; + ssn += diff; + + /* update bitmap */ + for (i = 0; i < tid_agg_rx->buf_size; i++) { + int index = (ssn + i) % tid_agg_rx->buf_size; + + tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index); + if (filtered & BIT_ULL(i)) + tid_agg_rx->reorder_buf_filtered |= BIT_ULL(index); + } + + /* now process also frames that the filter marking released */ + ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames); + +release: + spin_unlock_bh(&tid_agg_rx->reorder_lock); + + ieee80211_rx_handlers(&rx, &frames); + + out: + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee80211_mark_rx_ba_filtered_frames); + /* main receive path */ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index f4d38994ecee..053f5c4fa495 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -168,6 +168,8 @@ struct tid_ampdu_tx { * * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an * A-MSDU with individually reported subframes. + * @reorder_buf_filtered: bitmap indicating where there are filtered frames in + * the reorder buffer that should be ignored when releasing frames * @reorder_time: jiffies when skb was added * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) * @reorder_timer: releases expired frames from the reorder buffer. @@ -195,6 +197,7 @@ struct tid_ampdu_tx { struct tid_ampdu_rx { struct rcu_head rcu_head; spinlock_t reorder_lock; + u64 reorder_buf_filtered; struct sk_buff_head *reorder_buf; unsigned long *reorder_time; struct timer_list session_timer; -- cgit From 23665aaf9170ae6328cc4f68250c529a628af2ab Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 1 Feb 2016 11:40:55 +0200 Subject: mac80211: Interoperability workaround for 80+80 and 160 MHz channels Number of deployed 80 MHz capable VHT stations that do not support 80+80 and 160 MHz bandwidths seem to misbehave when trying to connect to an AP that advertises 80+80 or 160 MHz channel bandwidth in the VHT Operation element. To avoid such issues with deployed devices, modify the design based on recently accepted IEEE 802.11 standard changes (*). This allows poorly implemented VHT 80 MHz stations to connect with the AP in 80 MHz mode. 80+80 and 160 MHz capable stations need to support the new workaround mechanism to allow full bandwidth to be used. However, there are more or less no impacted station with 80+80/160 capability deployed. The rebased version of this patch is based on the updated version from Johannes Berg to take the HT/VHT chandef refactoring into account. (*) Changes in https://mentor.ieee.org/802.11/dcn/15/11-15-1530-04-000m-vht160-operation-signaling-through-non-zero-ccfs1.docx were accepted during the IEEE 802.11 January 2016 meeting. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/util.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 091f3dd62ad1..f1e5b76eda70 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2364,10 +2364,23 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, switch (chandef->width) { case NL80211_CHAN_WIDTH_160: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; + /* + * Convert 160 MHz channel width to new style as interop + * workaround. + */ + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + vht_oper->center_freq_seg2_idx = vht_oper->center_freq_seg1_idx; + if (chandef->chan->center_freq < chandef->center_freq1) + vht_oper->center_freq_seg1_idx -= 8; + else + vht_oper->center_freq_seg1_idx += 8; break; case NL80211_CHAN_WIDTH_80P80: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; + /* + * Convert 80+80 MHz channel width to new style as interop + * workaround. + */ + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; break; case NL80211_CHAN_WIDTH_80: vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; @@ -2430,6 +2443,20 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, case IEEE80211_VHT_CHANWIDTH_80MHZ: new.width = NL80211_CHAN_WIDTH_80; new.center_freq1 = cf1; + /* If needed, adjust based on the newer interop workaround. */ + if (oper->center_freq_seg2_idx) { + unsigned int diff; + + diff = abs(oper->center_freq_seg2_idx - + oper->center_freq_seg1_idx); + if (diff == 8) { + new.width = NL80211_CHAN_WIDTH_160; + new.center_freq1 = cf2; + } else if (diff > 8) { + new.width = NL80211_CHAN_WIDTH_80P80; + new.center_freq2 = cf2; + } + } break; case IEEE80211_VHT_CHANWIDTH_160MHZ: new.width = NL80211_CHAN_WIDTH_160; -- cgit From ea32f065bd3e3e09f0bcb3042f1664caf6b3e233 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 2 Feb 2016 13:21:14 +0530 Subject: mac80211: fix memory leak On error we jumped to the error label and returned the error code but we missed releasing sinfo. Fixes: 5fe74014172d ("mac80211: avoid excessive stack usage in sta_info") Reviewed-by: Julian Calaby Signed-off-by: Sudip Mukherjee Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 5894c0a1c01f..87b7e7a7df6c 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -562,6 +562,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) __cleanup_single_sta(sta); out_err: mutex_unlock(&local->sta_mtx); + kfree(sinfo); rcu_read_lock(); return err; } -- cgit From 88665f5a797a832ff7926ad2287ee41738bc09b9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 2 Feb 2016 14:39:08 +0100 Subject: mac80211: move A-MSDU skb_linearize call to ieee80211_amsdu_to_8023s Prepararation for zero-copy A-MSDU support with page fragment SKBs Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 3 --- net/wireless/util.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9fb7074f0280..44192143ebdf 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2223,9 +2223,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) skb->dev = dev; __skb_queue_head_init(&frame_list); - if (skb_linearize(skb)) - return RX_DROP_UNUSABLE; - ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, rx->sdata->vif.type, rx->local->hw.extra_tx_headroom, true); diff --git a/net/wireless/util.c b/net/wireless/util.c index 92770427b211..6a407699a900 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -657,6 +657,9 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, int remaining, err; u8 dst[ETH_ALEN], src[ETH_ALEN]; + if (skb_linearize(skb)) + goto out; + if (has_80211_header) { err = ieee80211_data_to_8023(skb, addr, iftype); if (err) -- cgit From 2d1c304cb2d5cf28d8362496bd9ea6aadf4f01d2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 2 Feb 2016 14:39:09 +0100 Subject: cfg80211: add function for 802.3 conversion with separate output buffer Use skb_copy_bits in preparation for allowing fragmented skbs Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/util.c | 106 ++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 6a407699a900..f75f6010eb08 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -393,9 +393,9 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); -unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) +static unsigned int __ieee80211_get_mesh_hdrlen(u8 flags) { - int ae = meshhdr->flags & MESH_FLAGS_AE; + int ae = flags & MESH_FLAGS_AE; /* 802.11-2012, 8.2.4.7.3 */ switch (ae) { default: @@ -407,21 +407,31 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) return 18; } } + +unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) +{ + return __ieee80211_get_mesh_hdrlen(meshhdr->flags); +} EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen); -int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype) +static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr, + const u8 *addr, enum nl80211_iftype iftype) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 hdrlen, ethertype; - u8 *payload; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN] __aligned(2); + struct { + u8 hdr[ETH_ALEN] __aligned(2); + __be16 proto; + } payload; + struct ethhdr tmp; + u16 hdrlen; + u8 mesh_flags = 0; if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return -1; hdrlen = ieee80211_hdrlen(hdr->frame_control); + if (skb->len < hdrlen + 8) + return -1; /* convert IEEE 802.11 header + possible LLC headers into Ethernet * header @@ -432,8 +442,11 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, * 1 0 BSSID SA DA n/a * 1 1 RA TA DA SA */ - memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); + memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN); + + if (iftype == NL80211_IFTYPE_MESH_POINT) + skb_copy_bits(skb, hdrlen, &mesh_flags, 1); switch (hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { @@ -450,44 +463,31 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, iftype != NL80211_IFTYPE_STATION)) return -1; if (iftype == NL80211_IFTYPE_MESH_POINT) { - struct ieee80211s_hdr *meshdr = - (struct ieee80211s_hdr *) (skb->data + hdrlen); - /* make sure meshdr->flags is on the linear part */ - if (!pskb_may_pull(skb, hdrlen + 1)) - return -1; - if (meshdr->flags & MESH_FLAGS_AE_A4) + if (mesh_flags & MESH_FLAGS_AE_A4) return -1; - if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { + if (mesh_flags & MESH_FLAGS_AE_A5_A6) { skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr1), - dst, ETH_ALEN); - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr2), - src, ETH_ALEN); + tmp.h_dest, 2 * ETH_ALEN); } - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags); } break; case cpu_to_le16(IEEE80211_FCTL_FROMDS): if ((iftype != NL80211_IFTYPE_STATION && iftype != NL80211_IFTYPE_P2P_CLIENT && iftype != NL80211_IFTYPE_MESH_POINT) || - (is_multicast_ether_addr(dst) && - ether_addr_equal(src, addr))) + (is_multicast_ether_addr(tmp.h_dest) && + ether_addr_equal(tmp.h_source, addr))) return -1; if (iftype == NL80211_IFTYPE_MESH_POINT) { - struct ieee80211s_hdr *meshdr = - (struct ieee80211s_hdr *) (skb->data + hdrlen); - /* make sure meshdr->flags is on the linear part */ - if (!pskb_may_pull(skb, hdrlen + 1)) + if (mesh_flags & MESH_FLAGS_AE_A5_A6) return -1; - if (meshdr->flags & MESH_FLAGS_AE_A5_A6) - return -1; - if (meshdr->flags & MESH_FLAGS_AE_A4) + if (mesh_flags & MESH_FLAGS_AE_A4) skb_copy_bits(skb, hdrlen + offsetof(struct ieee80211s_hdr, eaddr1), - src, ETH_ALEN); - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); + tmp.h_source, ETH_ALEN); + hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags); } break; case cpu_to_le16(0): @@ -498,33 +498,33 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, break; } - if (!pskb_may_pull(skb, hdrlen + 8)) - return -1; - - payload = skb->data + hdrlen; - ethertype = (payload[6] << 8) | payload[7]; + skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)); + tmp.h_proto = payload.proto; - if (likely((ether_addr_equal(payload, rfc1042_header) && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - ether_addr_equal(payload, bridge_tunnel_header))) { + if (likely((ether_addr_equal(payload.hdr, rfc1042_header) && + tmp.h_proto != htons(ETH_P_AARP) && + tmp.h_proto != htons(ETH_P_IPX)) || + ether_addr_equal(payload.hdr, bridge_tunnel_header))) /* remove RFC1042 or Bridge-Tunnel encapsulation and * replace EtherType */ - skb_pull(skb, hdrlen + 6); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } else { - struct ethhdr *ehdr; - __be16 len; + hdrlen += ETH_ALEN + 2; + else + tmp.h_proto = htons(skb->len); + + pskb_pull(skb, hdrlen); - skb_pull(skb, hdrlen); - len = htons(skb->len); + if (!ehdr) ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); - memcpy(ehdr->h_dest, dst, ETH_ALEN); - memcpy(ehdr->h_source, src, ETH_ALEN); - ehdr->h_proto = len; - } + memcpy(ehdr, &tmp, sizeof(tmp)); + return 0; } + +int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, + enum nl80211_iftype iftype) +{ + return __ieee80211_data_to_8023(skb, NULL, addr, iftype); +} EXPORT_SYMBOL(ieee80211_data_to_8023); int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, -- cgit From 230fd28a91542f0abfe390bda74702a3504c3df6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 2 Feb 2016 14:39:10 +0100 Subject: cfg80211: add support for non-linear skbs in ieee80211_amsdu_to_8023s Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/util.c | 100 +++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index f75f6010eb08..6e4eb3555177 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -644,73 +644,75 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, } EXPORT_SYMBOL(ieee80211_data_from_8023); +static struct sk_buff * +__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen, + int offset, int len) +{ + struct sk_buff *frame; + + if (skb->len - offset < len) + return NULL; + + /* + * Allocate and reserve two bytes more for payload + * alignment since sizeof(struct ethhdr) is 14. + */ + frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len); + + skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); + skb_copy_bits(skb, offset, skb_put(frame, len), len); + + return frame; +} void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, const u8 *addr, enum nl80211_iftype iftype, const unsigned int extra_headroom, bool has_80211_header) { + unsigned int hlen = ALIGN(extra_headroom, 4); struct sk_buff *frame = NULL; u16 ethertype; u8 *payload; - const struct ethhdr *eth; - int remaining, err; - u8 dst[ETH_ALEN], src[ETH_ALEN]; - - if (skb_linearize(skb)) - goto out; + int offset = 0, remaining, err; + struct ethhdr eth; + bool reuse_skb = true; + bool last = false; if (has_80211_header) { - err = ieee80211_data_to_8023(skb, addr, iftype); + err = __ieee80211_data_to_8023(skb, ð, addr, iftype); if (err) goto out; - - /* skip the wrapping header */ - eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); - if (!eth) - goto out; - } else { - eth = (struct ethhdr *) skb->data; } - while (skb != frame) { + while (!last) { + unsigned int subframe_len; + int len; u8 padding; - __be16 len = eth->h_proto; - unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); - - remaining = skb->len; - memcpy(dst, eth->h_dest, ETH_ALEN); - memcpy(src, eth->h_source, ETH_ALEN); + skb_copy_bits(skb, offset, ð, sizeof(eth)); + len = ntohs(eth.h_proto); + subframe_len = sizeof(struct ethhdr) + len; padding = (4 - subframe_len) & 0x3; + /* the last MSDU has no padding */ + remaining = skb->len - offset; if (subframe_len > remaining) goto purge; - skb_pull(skb, sizeof(struct ethhdr)); + offset += sizeof(struct ethhdr); /* reuse skb for the last subframe */ - if (remaining <= subframe_len + padding) + last = remaining <= subframe_len + padding; + if (!skb_is_nonlinear(skb) && last) { + skb_pull(skb, offset); frame = skb; - else { - unsigned int hlen = ALIGN(extra_headroom, 4); - /* - * Allocate and reserve two bytes more for payload - * alignment since sizeof(struct ethhdr) is 14. - */ - frame = dev_alloc_skb(hlen + subframe_len + 2); + reuse_skb = true; + } else { + frame = __ieee80211_amsdu_copy(skb, hlen, offset, len); if (!frame) goto purge; - skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); - memcpy(skb_put(frame, ntohs(len)), skb->data, - ntohs(len)); - - eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + - padding); - if (!eth) { - dev_kfree_skb(frame); - goto purge; - } + offset += len + padding; } skb_reset_network_header(frame); @@ -719,24 +721,20 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, payload = frame->data; ethertype = (payload[6] << 8) | payload[7]; - if (likely((ether_addr_equal(payload, rfc1042_header) && ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || ether_addr_equal(payload, bridge_tunnel_header))) { - /* remove RFC1042 or Bridge-Tunnel - * encapsulation and replace EtherType */ - skb_pull(frame, 6); - memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); - } else { - memcpy(skb_push(frame, sizeof(__be16)), &len, - sizeof(__be16)); - memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); + eth.h_proto = htons(ethertype); + skb_pull(frame, ETH_ALEN + 2); } + + memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth)); __skb_queue_tail(list, frame); } + if (!reuse_skb) + dev_kfree_skb(skb); + return; purge: -- cgit From 34d505193bd10668acf1caba02d2f66bddc23fea Mon Sep 17 00:00:00 2001 From: Lior David Date: Thu, 28 Jan 2016 10:58:25 +0200 Subject: cfg80211: basic support for PBSS network type PBSS (Personal Basic Service Set) is a new BSS type for DMG networks. It is similar to infrastructure BSS, having an AP-like entity called PCP (PBSS Control Point), but it has few differences. PBSS support is mandatory for 11ad devices. Add support for PBSS by introducing a new PBSS flag attribute. The PBSS flag is used in the START_AP command to request starting a PCP instead of an AP, and in the CONNECT command to request connecting to a PCP instead of an AP. Signed-off-by: Lior David Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 10 ++++++++++ include/net/cfg80211.h | 8 ++++++++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/nl80211.c | 11 +++++++++++ net/wireless/sme.c | 9 ++++++--- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 20d07ef679e8..1f231cd08138 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -422,6 +422,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, if (sme->privacy && !rsn_eid) wil_info(wil, "WSC connection\n"); + if (sme->pbss) { + wil_err(wil, "connect - PBSS not yet supported\n"); + return -EOPNOTSUPP; + } + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); @@ -870,6 +875,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } + if (info->pbss) { + wil_err(wil, "AP: PBSS not yet supported\n"); + return -EOPNOTSUPP; + } + switch (info->hidden_ssid) { case NL80211_HIDDEN_SSID_NOT_IN_USE: hidden_ssid = WMI_HIDDEN_SSID_DISABLED; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9bcaaf7cd15a..9e1b24c29f0c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -712,6 +712,8 @@ struct cfg80211_acl_data { * @p2p_opp_ps: P2P opportunistic PS * @acl: ACL configuration used by the drivers which has support for * MAC address based access control + * @pbss: If set, start as a PCP instead of AP. Relevant for DMG + * networks. */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -730,6 +732,7 @@ struct cfg80211_ap_settings { u8 p2p_ctwindow; bool p2p_opp_ps; const struct cfg80211_acl_data *acl; + bool pbss; }; /** @@ -1888,6 +1891,8 @@ struct cfg80211_ibss_params { * @ht_capa_mask: The bits of ht_capa which are to be used. * @vht_capa: VHT Capability overrides * @vht_capa_mask: The bits of vht_capa which are to be used. + * @pbss: if set, connect to a PCP instead of AP. Valid for DMG + * networks. */ struct cfg80211_connect_params { struct ieee80211_channel *channel; @@ -1910,6 +1915,7 @@ struct cfg80211_connect_params { struct ieee80211_ht_cap ht_capa_mask; struct ieee80211_vht_cap vht_capa; struct ieee80211_vht_cap vht_capa_mask; + bool pbss; }; /** @@ -3489,6 +3495,7 @@ struct cfg80211_cached_keys; * registered for unexpected class 3 frames (AP mode) * @conn: (private) cfg80211 software SME connection state machine data * @connect_keys: (private) keys to set after connection is established + * @conn_bss_type: connecting/connected BSS type * @ibss_fixed: (private) IBSS is using fixed BSSID * @ibss_dfs_possible: (private) IBSS may change to a DFS channel * @event_list: (private) list for internal event processing @@ -3519,6 +3526,7 @@ struct wireless_dev { u8 ssid_len, mesh_id_len, mesh_id_up_len; struct cfg80211_conn *conn; struct cfg80211_cached_keys *connect_keys; + enum ieee80211_bss_type conn_bss_type; struct list_head event_list; spinlock_t event_lock; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5b7b5ebe7ca8..7758969a2a8e 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1789,6 +1789,10 @@ enum nl80211_commands { * thus it must not specify the number of iterations, only the interval * between scans. The scan plans are executed sequentially. * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan. + * @NL80211_ATTR_PBSS: flag attribute. If set it means operate + * in a PBSS. Specified in %NL80211_CMD_CONNECT to request + * connecting to a PCP, and in %NL80211_CMD_START_AP to start + * a PCP instead of AP. Relevant for DMG networks only. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -2164,6 +2168,8 @@ enum nl80211_attrs { NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, NL80211_ATTR_SCHED_SCAN_PLANS, + NL80211_ATTR_PBSS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d4786f2802aa..268cb493f6a5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -401,6 +401,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 }, [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, + [NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -3461,6 +3462,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(params.acl); } + params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]); + if (params.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ]) + return -EOPNOTSUPP; + wdev_lock(wdev); err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { @@ -7980,6 +7985,12 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) connect.flags |= ASSOC_REQ_USE_RRM; } + connect.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]); + if (connect.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ]) { + kzfree(connkeys); + return -EOPNOTSUPP; + } + wdev_lock(dev->ieee80211_ptr); err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8020b5b094d4..79bd3a171caa 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -264,7 +264,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) wdev->conn->params.bssid, wdev->conn->params.ssid, wdev->conn->params.ssid_len, - IEEE80211_BSS_TYPE_ESS, + wdev->conn_bss_type, IEEE80211_PRIVACY(wdev->conn->params.privacy)); if (!bss) return NULL; @@ -687,7 +687,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, - IEEE80211_BSS_TYPE_ESS, + wdev->conn_bss_type, IEEE80211_PRIVACY_ANY); if (bss) cfg80211_hold_bss(bss_from_pub(bss)); @@ -846,7 +846,7 @@ void cfg80211_roamed(struct net_device *dev, bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, wdev->ssid_len, - IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); + wdev->conn_bss_type, IEEE80211_PRIVACY_ANY); if (WARN_ON(!bss)) return; @@ -1017,6 +1017,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, memcpy(wdev->ssid, connect->ssid, connect->ssid_len); wdev->ssid_len = connect->ssid_len; + wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS : + IEEE80211_BSS_TYPE_ESS; + if (!rdev->ops->connect) err = cfg80211_sme_connect(wdev, connect, prev_bssid); else -- cgit From bf5a70e105820eb214d3f33376cf6a3011d2e20c Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 3 Feb 2016 13:58:36 +0100 Subject: mac80211: Remove MPP table entries with MPath Make the mesh_path_del() function remove all mpp table entries that are proxied by the removed mesh path. Acked-by: Bob Copeland Signed-off-by: Henning Rogge Signed-off-by: Johannes Berg --- net/mac80211/mesh_pathtbl.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index c95bdb8bb452..40ed206b0f60 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -834,6 +834,29 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta) rcu_read_unlock(); } +static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata, + const u8 *proxy) +{ + struct mesh_table *tbl; + struct mesh_path *mpp; + struct mpath_node *node; + int i; + + rcu_read_lock(); + read_lock_bh(&pathtbl_resize_lock); + tbl = resize_dereference_mpp_paths(); + for_each_mesh_entry(tbl, node, i) { + mpp = node->mpath; + if (ether_addr_equal(mpp->mpp, proxy)) { + spin_lock(&tbl->hashwlock[i]); + __mesh_path_del(tbl, node); + spin_unlock(&tbl->hashwlock[i]); + } + } + read_unlock_bh(&pathtbl_resize_lock); + rcu_read_unlock(); +} + static void table_flush_by_iface(struct mesh_table *tbl, struct ieee80211_sub_if_data *sdata) { @@ -891,6 +914,9 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) int hash_idx; int err = 0; + /* flush relevant mpp entries first */ + mpp_flush_by_proxy(sdata, addr); + read_lock_bh(&pathtbl_resize_lock); tbl = resize_dereference_mesh_paths(); hash_idx = mesh_table_hash(addr, sdata, tbl); -- cgit From ab1c7906b0bead549065a3a2def5aeda21818c99 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 3 Feb 2016 13:58:37 +0100 Subject: mac80211: let unused MPP table entries timeout Remember the last time when a mpp table entry is used for rx or tx and remove them after MESH_PATH_EXPIRE time. Acked-by: Bob Copeland Signed-off-by: Henning Rogge Signed-off-by: Johannes Berg --- net/mac80211/mesh_pathtbl.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/rx.c | 1 + net/mac80211/tx.c | 5 ++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 40ed206b0f60..29e50407507a 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -940,6 +940,46 @@ enddel: return err; } +/** + * mpp_path_del - delete a mesh proxy path from the table + * + * @addr: addr address (ETH_ALEN length) + * @sdata: local subif + * + * Returns: 0 if successful + */ +static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) +{ + struct mesh_table *tbl; + struct mesh_path *mpath; + struct mpath_node *node; + struct hlist_head *bucket; + int hash_idx; + int err = 0; + + read_lock_bh(&pathtbl_resize_lock); + tbl = resize_dereference_mpp_paths(); + hash_idx = mesh_table_hash(addr, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; + + spin_lock(&tbl->hashwlock[hash_idx]); + hlist_for_each_entry(node, bucket, list) { + mpath = node->mpath; + if (mpath->sdata == sdata && + ether_addr_equal(addr, mpath->dst)) { + __mesh_path_del(tbl, node); + goto enddel; + } + } + + err = -ENXIO; +enddel: + mesh_paths_generation++; + spin_unlock(&tbl->hashwlock[hash_idx]); + read_unlock_bh(&pathtbl_resize_lock); + return err; +} + /** * mesh_path_tx_pending - sends pending frames in a mesh path queue * @@ -1154,6 +1194,17 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) mesh_path_del(mpath->sdata, mpath->dst); } + + tbl = rcu_dereference(mpp_paths); + for_each_mesh_entry(tbl, node, i) { + if (node->mpath->sdata != sdata) + continue; + mpath = node->mpath; + if ((!(mpath->flags & MESH_PATH_FIXED)) && + time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) + mpp_path_del(mpath->sdata, mpath->dst); + } + rcu_read_unlock(); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 44192143ebdf..91279576f4a7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2311,6 +2311,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) spin_lock_bh(&mppath->state_lock); if (!ether_addr_equal(mppath->mpp, mpp_addr)) memcpy(mppath->mpp, mpp_addr, ETH_ALEN); + mppath->exp_time = jiffies; spin_unlock_bh(&mppath->state_lock); } rcu_read_unlock(); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a5aa275d0434..7bb67fa9f4d2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2173,8 +2173,11 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, mpp_lookup = true; } - if (mpp_lookup) + if (mpp_lookup) { mppath = mpp_path_lookup(sdata, skb->data); + if (mppath) + mppath->exp_time = jiffies; + } if (mppath && mpath) mesh_path_del(mpath->sdata, mpath->dst); -- cgit From 4cc955de7c99fe60b09688d7c2564f8fc7d00ca2 Mon Sep 17 00:00:00 2001 From: Henning Rogge Date: Wed, 3 Feb 2016 13:58:38 +0100 Subject: mac80211: Unify mesh and mpp path removal function mpp_path_del() and mesh_path_del() are mostly the same function. Move common code into a new static function. Acked-by: Bob Copeland Signed-off-by: Henning Rogge Signed-off-by: Johannes Berg --- net/mac80211/mesh_pathtbl.c | 77 ++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 29e50407507a..2ba7aa56b11c 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -55,16 +55,21 @@ int mpp_paths_generation; static DEFINE_RWLOCK(pathtbl_resize_lock); +static inline struct mesh_table *resize_dereference_paths( + struct mesh_table __rcu *table) +{ + return rcu_dereference_protected(table, + lockdep_is_held(&pathtbl_resize_lock)); +} + static inline struct mesh_table *resize_dereference_mesh_paths(void) { - return rcu_dereference_protected(mesh_paths, - lockdep_is_held(&pathtbl_resize_lock)); + return resize_dereference_paths(mesh_paths); } static inline struct mesh_table *resize_dereference_mpp_paths(void) { - return rcu_dereference_protected(mpp_paths, - lockdep_is_held(&pathtbl_resize_lock)); + return resize_dereference_paths(mpp_paths); } /* @@ -898,14 +903,17 @@ void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) } /** - * mesh_path_del - delete a mesh path from the table + * table_path_del - delete a path from the mesh or mpp table * - * @addr: dst address (ETH_ALEN length) + * @tbl: mesh or mpp path table * @sdata: local subif + * @addr: dst address (ETH_ALEN length) * * Returns: 0 if successful */ -int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) +static int table_path_del(struct mesh_table __rcu *rcu_tbl, + struct ieee80211_sub_if_data *sdata, + const u8 *addr) { struct mesh_table *tbl; struct mesh_path *mpath; @@ -914,11 +922,7 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) int hash_idx; int err = 0; - /* flush relevant mpp entries first */ - mpp_flush_by_proxy(sdata, addr); - - read_lock_bh(&pathtbl_resize_lock); - tbl = resize_dereference_mesh_paths(); + tbl = resize_dereference_paths(rcu_tbl); hash_idx = mesh_table_hash(addr, sdata, tbl); bucket = &tbl->hash_buckets[hash_idx]; @@ -934,9 +938,30 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) err = -ENXIO; enddel: - mesh_paths_generation++; spin_unlock(&tbl->hashwlock[hash_idx]); + return err; +} + +/** + * mesh_path_del - delete a mesh path from the table + * + * @addr: dst address (ETH_ALEN length) + * @sdata: local subif + * + * Returns: 0 if successful + */ +int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) +{ + int err = 0; + + /* flush relevant mpp entries first */ + mpp_flush_by_proxy(sdata, addr); + + read_lock_bh(&pathtbl_resize_lock); + err = table_path_del(mesh_paths, sdata, addr); + mesh_paths_generation++; read_unlock_bh(&pathtbl_resize_lock); + return err; } @@ -950,33 +975,13 @@ enddel: */ static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) { - struct mesh_table *tbl; - struct mesh_path *mpath; - struct mpath_node *node; - struct hlist_head *bucket; - int hash_idx; int err = 0; read_lock_bh(&pathtbl_resize_lock); - tbl = resize_dereference_mpp_paths(); - hash_idx = mesh_table_hash(addr, sdata, tbl); - bucket = &tbl->hash_buckets[hash_idx]; - - spin_lock(&tbl->hashwlock[hash_idx]); - hlist_for_each_entry(node, bucket, list) { - mpath = node->mpath; - if (mpath->sdata == sdata && - ether_addr_equal(addr, mpath->dst)) { - __mesh_path_del(tbl, node); - goto enddel; - } - } - - err = -ENXIO; -enddel: - mesh_paths_generation++; - spin_unlock(&tbl->hashwlock[hash_idx]); + err = table_path_del(mpp_paths, sdata, addr); + mpp_paths_generation++; read_unlock_bh(&pathtbl_resize_lock); + return err; } -- cgit From 0241fa19ef4021c3db6f70b8f08d69f9e21cc043 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 3 Feb 2016 21:52:23 +0200 Subject: mac80211: fix VHT MU-MIMO frame processing The source and destintation addresses in the memcpy arguments are flipped. Fix that. Fixes: 23a1f8d44c0b("mac80211: process and save VHT MU-MIMO group frame") Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/vht.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 204cf9ad3019..341d192cea52 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -459,10 +459,12 @@ void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN)) return; - memcpy(mgmt->u.action.u.vht_group_notif.membership, - bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN); - memcpy(mgmt->u.action.u.vht_group_notif.position, - bss_conf->mu_group.position, WLAN_USER_POSITION_LEN); + memcpy(bss_conf->mu_group.membership, + mgmt->u.action.u.vht_group_notif.membership, + WLAN_MEMBERSHIP_LEN); + memcpy(bss_conf->mu_group.position, + mgmt->u.action.u.vht_group_notif.position, + WLAN_USER_POSITION_LEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); } -- cgit From 2bf0ccc7095e6cf665bbdb7c32c352d24f1ae033 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 8 Feb 2016 14:25:26 +0100 Subject: cfg80211: fix faulty variable initialization in ieee80211_amsdu_to_8023s reuse_skb is set to true if the code decides to use the last segment. Fixes a memory leak Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 6e4eb3555177..9880c894c58d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -676,7 +676,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, u8 *payload; int offset = 0, remaining, err; struct ethhdr eth; - bool reuse_skb = true; + bool reuse_skb = false; bool last = false; if (has_80211_header) { -- cgit From 2b67f944f88c29e71efdcfc4d0c21ab28fbf23ae Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 8 Feb 2016 14:34:42 +0100 Subject: cfg80211: reuse existing page fragments in A-MSDU rx This massively reduces data copying and thus improves rx performance Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/util.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 9880c894c58d..c7f6820bb258 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, } EXPORT_SYMBOL(ieee80211_data_from_8023); +static void +__frame_add_frag(struct sk_buff *skb, struct page *page, + void *ptr, int len, int size) +{ + struct skb_shared_info *sh = skb_shinfo(skb); + int page_offset; + + atomic_inc(&page->_count); + page_offset = ptr - page_address(page); + skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size); +} + +static void +__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, + int offset, int len) +{ + struct skb_shared_info *sh = skb_shinfo(skb); + const skb_frag_t *frag = &sh->frags[-1]; + struct page *frag_page; + void *frag_ptr; + int frag_len, frag_size; + int head_size = skb->len - skb->data_len; + int cur_len; + + frag_page = virt_to_head_page(skb->head); + frag_ptr = skb->data; + frag_size = head_size; + + while (offset >= frag_size) { + offset -= frag_size; + frag++; + frag_page = skb_frag_page(frag); + frag_ptr = skb_frag_address(frag); + frag_size = skb_frag_size(frag); + } + + frag_ptr += offset; + frag_len = frag_size - offset; + + cur_len = min(len, frag_len); + + __frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size); + len -= cur_len; + + while (len > 0) { + frag++; + frag_len = skb_frag_size(frag); + cur_len = min(len, frag_len); + __frame_add_frag(frame, skb_frag_page(frag), + skb_frag_address(frag), cur_len, frag_len); + len -= cur_len; + } +} + static struct sk_buff * __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen, - int offset, int len) + int offset, int len, bool reuse_frag) { struct sk_buff *frame; + int cur_len = len; if (skb->len - offset < len) return NULL; + /* + * When reusing framents, copy some data to the head to simplify + * ethernet header handling and speed up protocol header processing + * in the stack later. + */ + if (reuse_frag) + cur_len = min_t(int, len, 32); + /* * Allocate and reserve two bytes more for payload * alignment since sizeof(struct ethhdr) is 14. */ - frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len); + frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len); skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); - skb_copy_bits(skb, offset, skb_put(frame, len), len); + skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len); + + len -= cur_len; + if (!len) + return frame; + + offset += cur_len; + __ieee80211_amsdu_copy_frag(skb, frame, offset, len); return frame; } @@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, u8 *payload; int offset = 0, remaining, err; struct ethhdr eth; + bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb); bool reuse_skb = false; bool last = false; @@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, offset += sizeof(struct ethhdr); /* reuse skb for the last subframe */ last = remaining <= subframe_len + padding; - if (!skb_is_nonlinear(skb) && last) { + if (!skb_is_nonlinear(skb) && !reuse_frag && last) { skb_pull(skb, offset); frame = skb; reuse_skb = true; } else { - frame = __ieee80211_amsdu_copy(skb, hlen, offset, len); + frame = __ieee80211_amsdu_copy(skb, hlen, offset, len, + reuse_frag); if (!frame) goto purge; -- cgit From 109843b07aed8663a3c1fe9ef02c540b6ff203b4 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 19 Feb 2016 12:18:01 +0100 Subject: mac80211: fix wiphy supported_band access Fix wiphy supported_band access in tx radiotap parsing introduced in commit 5ec3aed9ba4c ("mac80211: Parse legacy and HT rate in injected frames"). In particular, info->band is always set to 0 (IEEE80211_BAND_2GHZ) since it has not assigned yet. This cause a kernel crash on 5GHz only devices. Move ieee80211_parse_tx_radiotap() after info->band assignment Signed-off-by: Lorenzo Bianconi Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7bb67fa9f4d2..3a7475ff1a41 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1892,10 +1892,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_CTL_INJECTED; - /* process and remove the injection radiotap header */ - if (!ieee80211_parse_tx_radiotap(local, skb)) - goto fail; - rcu_read_lock(); /* @@ -1957,6 +1953,11 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, goto fail_rcu; info->band = chandef->chan->band; + + /* process and remove the injection radiotap header */ + if (!ieee80211_parse_tx_radiotap(local, skb)) + goto fail_rcu; + ieee80211_xmit(sdata, NULL, skb); rcu_read_unlock(); -- cgit From f8079d43cf0f1f0171606e75fcef6fe17bb183f2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 14 Feb 2016 13:56:35 +0200 Subject: mac80211: move TKIP TX IVs to public part of key struct Some drivers/devices might want to set the IVs by themselves (and still let mac80211 generate MMIC). Specifically, this is needed when the device does offloading at certain times, and the driver has to make sure that the IVs of new tx frames (from the host) are synchronized with IVs that were potentially used during the offloading. Similarly to CCMP, move the TX IVs of TKIP keys to the public part of the key struct, and export a function to add the IV right into the crypto header. The public tx_pn field is defined as atomic64, so define TKIP_PN_TO_IV16/32 helper macros to convert it to iv16/32 when needed. Since the iv32 used for the p1k cache is taken directly from the frame, we can safely remove iv16/32 from being protected by tkip.txlock. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 23 ++++++++++++++++++++--- net/mac80211/cfg.c | 5 +++-- net/mac80211/debugfs_key.c | 5 +++-- net/mac80211/key.c | 9 +++++---- net/mac80211/key.h | 10 +++++++--- net/mac80211/tkip.c | 36 ++++++++++++++++++------------------ net/mac80211/tkip.h | 2 -- net/mac80211/wpa.c | 11 ++++------- 8 files changed, 60 insertions(+), 41 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 57147749ae42..15879b49baad 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1521,9 +1521,8 @@ enum ieee80211_key_flags { * wants to be given when a frame is transmitted and needs to be * encrypted in hardware. * @cipher: The key's cipher suite selector. - * @tx_pn: PN used for TX on non-TKIP keys, may be used by the driver - * as well if it needs to do software PN assignment by itself - * (e.g. due to TSO) + * @tx_pn: PN used for TX keys, may be used by the driver as well if it + * needs to do software PN assignment by itself (e.g. due to TSO) * @flags: key flags, see &enum ieee80211_key_flags. * @keyidx: the key index (0-3) * @keylen: key material length @@ -1549,6 +1548,9 @@ struct ieee80211_key_conf { #define IEEE80211_MAX_PN_LEN 16 +#define TKIP_PN_TO_IV16(pn) ((u16)(pn & 0xffff)) +#define TKIP_PN_TO_IV32(pn) ((u32)((pn >> 16) & 0xffffffff)) + /** * struct ieee80211_key_seq - key sequence counter * @@ -4446,6 +4448,21 @@ void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, struct sk_buff *skb, u8 *p2k); +/** + * ieee80211_tkip_add_iv - write TKIP IV and Ext. IV to pos + * + * @pos: start of crypto header + * @keyconf: the parameter passed with the set key + * @pn: PN to add + * + * Returns: pointer to the octet following IVs (i.e. beginning of + * the packet payload) + * + * This function writes the tkip IV value to pos (which should + * point to the crypto header) + */ +u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key_conf *keyconf, u64 pn); + /** * ieee80211_get_key_tx_seq - get key TX sequence counter * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 66d22de93c8d..fe1704c4e8fb 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -339,8 +339,9 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: - iv32 = key->u.tkip.tx.iv32; - iv16 = key->u.tkip.tx.iv16; + pn64 = atomic64_read(&key->conf.tx_pn); + iv32 = TKIP_PN_TO_IV32(pn64); + iv16 = TKIP_PN_TO_IV16(pn64); if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE && !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 7961e7d0b61e..a2ef95f16f11 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -132,9 +132,10 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, len = scnprintf(buf, sizeof(buf), "\n"); break; case WLAN_CIPHER_SUITE_TKIP: + pn = atomic64_read(&key->conf.tx_pn); len = scnprintf(buf, sizeof(buf), "%08x %04x\n", - key->u.tkip.tx.iv32, - key->u.tkip.tx.iv16); + TKIP_PN_TO_IV32(pn), + TKIP_PN_TO_IV16(pn)); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 5e5bc599da4c..f9c4cb9c6e06 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -945,8 +945,9 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: - seq->tkip.iv32 = key->u.tkip.tx.iv32; - seq->tkip.iv16 = key->u.tkip.tx.iv16; + pn64 = atomic64_read(&key->conf.tx_pn); + seq->tkip.iv32 = TKIP_PN_TO_IV32(pn64); + seq->tkip.iv16 = TKIP_PN_TO_IV16(pn64); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: @@ -1039,8 +1040,8 @@ void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf, switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: - key->u.tkip.tx.iv32 = seq->tkip.iv32; - key->u.tkip.tx.iv16 = seq->tkip.iv16; + pn64 = (u64)seq->tkip.iv16 | ((u64)seq->tkip.iv32 << 16); + atomic64_set(&key->conf.tx_pn, pn64); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 9951ef06323e..4aa20cef0859 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -44,13 +44,17 @@ enum ieee80211_internal_tkip_state { }; struct tkip_ctx { - u32 iv32; /* current iv32 */ - u16 iv16; /* current iv16 */ u16 p1k[5]; /* p1k cache */ u32 p1k_iv32; /* iv32 for which p1k computed */ enum ieee80211_internal_tkip_state state; }; +struct tkip_ctx_rx { + struct tkip_ctx ctx; + u32 iv32; /* current iv32 */ + u16 iv16; /* current iv16 */ +}; + struct ieee80211_key { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; @@ -71,7 +75,7 @@ struct ieee80211_key { struct tkip_ctx tx; /* last received RSC */ - struct tkip_ctx rx[IEEE80211_NUM_TIDS]; + struct tkip_ctx_rx rx[IEEE80211_NUM_TIDS]; /* number of mic failures */ u32 mic_failures; diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 0ae207771a58..b3622823bad2 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -1,6 +1,7 @@ /* * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. + * Copyright (C) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -142,15 +143,14 @@ static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx, /* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets * of the IV. Returns pointer to the octet following IVs (i.e., beginning of * the packet payload). */ -u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key) +u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key_conf *keyconf, u64 pn) { - lockdep_assert_held(&key->u.tkip.txlock); - - pos = write_tkip_iv(pos, key->u.tkip.tx.iv16); - *pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */; - put_unaligned_le32(key->u.tkip.tx.iv32, pos); + pos = write_tkip_iv(pos, TKIP_PN_TO_IV16(pn)); + *pos++ = (keyconf->keyidx << 6) | (1 << 5) /* Ext IV */; + put_unaligned_le32(TKIP_PN_TO_IV32(pn), pos); return pos + 4; } +EXPORT_SYMBOL_GPL(ieee80211_tkip_add_iv); static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32) { @@ -250,6 +250,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, u8 rc4key[16], keyid, *pos = payload; int res; const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; + struct tkip_ctx_rx *rx_ctx = &key->u.tkip.rx[queue]; if (payload_len < 12) return -1; @@ -265,37 +266,36 @@ int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, if ((keyid >> 6) != key->conf.keyidx) return TKIP_DECRYPT_INVALID_KEYIDX; - if (key->u.tkip.rx[queue].state != TKIP_STATE_NOT_INIT && - (iv32 < key->u.tkip.rx[queue].iv32 || - (iv32 == key->u.tkip.rx[queue].iv32 && - iv16 <= key->u.tkip.rx[queue].iv16))) + if (rx_ctx->ctx.state != TKIP_STATE_NOT_INIT && + (iv32 < rx_ctx->iv32 || + (iv32 == rx_ctx->iv32 && iv16 <= rx_ctx->iv16))) return TKIP_DECRYPT_REPLAY; if (only_iv) { res = TKIP_DECRYPT_OK; - key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED; + rx_ctx->ctx.state = TKIP_STATE_PHASE1_HW_UPLOADED; goto done; } - if (key->u.tkip.rx[queue].state == TKIP_STATE_NOT_INIT || - key->u.tkip.rx[queue].iv32 != iv32) { + if (rx_ctx->ctx.state == TKIP_STATE_NOT_INIT || + rx_ctx->iv32 != iv32) { /* IV16 wrapped around - perform TKIP phase 1 */ - tkip_mixing_phase1(tk, &key->u.tkip.rx[queue], ta, iv32); + tkip_mixing_phase1(tk, &rx_ctx->ctx, ta, iv32); } if (key->local->ops->update_tkip_key && key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE && - key->u.tkip.rx[queue].state != TKIP_STATE_PHASE1_HW_UPLOADED) { + rx_ctx->ctx.state != TKIP_STATE_PHASE1_HW_UPLOADED) { struct ieee80211_sub_if_data *sdata = key->sdata; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(key->sdata->bss, struct ieee80211_sub_if_data, u.ap); drv_update_tkip_key(key->local, sdata, &key->conf, key->sta, - iv32, key->u.tkip.rx[queue].p1k); - key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED; + iv32, rx_ctx->ctx.p1k); + rx_ctx->ctx.state = TKIP_STATE_PHASE1_HW_UPLOADED; } - tkip_mixing_phase2(tk, &key->u.tkip.rx[queue], iv16, rc4key); + tkip_mixing_phase2(tk, &rx_ctx->ctx, iv16, rc4key); res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12); done: diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h index e3ecb659b90a..a1bcbfbefe7c 100644 --- a/net/mac80211/tkip.h +++ b/net/mac80211/tkip.h @@ -13,8 +13,6 @@ #include #include "key.h" -u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key); - int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, struct ieee80211_key *key, struct sk_buff *skb, diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index d824c38971ed..18848258adde 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -1,6 +1,7 @@ /* * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2008, Jouni Malinen + * Copyright (C) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -183,7 +184,6 @@ mic_fail_no_key: return RX_DROP_UNUSABLE; } - static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -191,6 +191,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); unsigned int hdrlen; int len, tail; + u64 pn; u8 *pos; if (info->control.hw_key && @@ -222,12 +223,8 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) return 0; /* Increase IV for the frame */ - spin_lock(&key->u.tkip.txlock); - key->u.tkip.tx.iv16++; - if (key->u.tkip.tx.iv16 == 0) - key->u.tkip.tx.iv32++; - pos = ieee80211_tkip_add_iv(pos, key); - spin_unlock(&key->u.tkip.txlock); + pn = atomic64_inc_return(&key->conf.tx_pn); + pos = ieee80211_tkip_add_iv(pos, &key->conf, pn); /* hwaccel - with software IV */ if (info->control.hw_key) -- cgit From 1ad4f639cc3e09d6c8402a0fcc592e2391683e31 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 14 Feb 2016 13:56:36 +0200 Subject: iwlwifi: mvm: move TX PN assignment for TKIP to the driver If protocol offloading is configured, the fw might generate some frames (e.g. arp response) on its own during d3/d0i3. On d3/d0i3 exit the driver queries the updated PN (if relevant), and updates its keys (for the d0i3 case, this is done by iwl_mvm_d0i3_exit_work(), which is scheduled on d0i3 exit) While in d0i3, iwlmvm defers tx frames until d0i3 exit, and then continues their processing. This is problematic with TKIP, since the frame's PN has already been set at this stage (in contrast to CCMP, where the PN is being set only later on), so both the frame's PN and the upcoming PN update (from d0i3 exit work) might be wrong. Fix it by moving the TX PN assignment (for TKIP) to the driver, similarly to CCMP. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 15 ++++++++++----- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 ++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index d3e21d95cece..93e495861214 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -249,16 +249,19 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, return; case WLAN_CIPHER_SUITE_TKIP: if (sta) { + u64 pn64; + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; rx_p1ks = data->tkip->rx_uni; - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + pn64 = atomic64_read(&key->tx_pn); + tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64)); + tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64)); - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); + ieee80211_get_tkip_p1k_iv(key, TKIP_PN_TO_IV32(pn64), + p1k); iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k); memcpy(data->tkip->mic_keys.tx, @@ -1601,7 +1604,9 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, case WLAN_CIPHER_SUITE_TKIP: iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); - ieee80211_set_key_tx_seq(key, &seq); + atomic64_set(&key->tx_pn, + (u64)seq.tkip.iv16 | + ((u64)seq.tkip.iv32 << 16)); break; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 1bd3f0b700d3..2b532925781a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2585,7 +2585,7 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; break; case WLAN_CIPHER_SUITE_CCMP: key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 8bf48a7d0f4e..ca1e485a6adc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -299,6 +299,8 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, case WLAN_CIPHER_SUITE_TKIP: tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + pn = atomic64_inc_return(&keyconf->tx_pn); + ieee80211_tkip_add_iv(crypto_hdr, keyconf, pn); ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); break; -- cgit From ca48ebbc7ea7e82e3ae4b55aacead0cdb54ff008 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 15 Feb 2016 12:34:10 +0200 Subject: mac80211: remove ieee80211_get_key_tx_seq/ieee80211_set_key_tx_seq Since the PNs of all the tx keys are now tracked in the public part of the key struct (with atomic counter), we no longer need these functions. dvm and vt665{5,6} are currently the only users of these functions, so update them accordingly. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 20 +++---- drivers/staging/vt6655/rxtx.c | 12 ++-- drivers/staging/vt6656/rxtx.c | 12 ++-- include/net/mac80211.h | 34 ----------- net/mac80211/key.c | 87 ---------------------------- 5 files changed, 24 insertions(+), 141 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index 4841be2aa499..1799469268ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -943,14 +943,16 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: if (sta) { + u64 pn64; + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; rx_p1ks = data->tkip->rx_uni; - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + pn64 = atomic64_read(&key->tx_pn); + tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64)); + tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64)); ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); @@ -996,19 +998,13 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, break; case WLAN_CIPHER_SUITE_CCMP: if (sta) { - u8 *pn = seq.ccmp.pn; + u64 pn64; aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - ieee80211_get_key_tx_seq(key, &seq); - aes_tx_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); + pn64 = atomic64_read(&key->tx_pn); + aes_tx_sc->pn = cpu_to_le64(pn64); } else aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c index b668db6a45fb..1a2dda09b69d 100644 --- a/drivers/staging/vt6655/rxtx.c +++ b/drivers/staging/vt6655/rxtx.c @@ -1210,7 +1210,7 @@ static void vnt_fill_txkey(struct ieee80211_hdr *hdr, u8 *key_buffer, struct sk_buff *skb, u16 payload_len, struct vnt_mic_hdr *mic_hdr) { - struct ieee80211_key_seq seq; + u64 pn64; u8 *iv = ((u8 *)hdr + ieee80211_get_hdrlen_from_skb(skb)); /* strip header and icv len from payload */ @@ -1243,9 +1243,13 @@ static void vnt_fill_txkey(struct ieee80211_hdr *hdr, u8 *key_buffer, mic_hdr->payload_len = cpu_to_be16(payload_len); ether_addr_copy(mic_hdr->mic_addr2, hdr->addr2); - ieee80211_get_key_tx_seq(tx_key, &seq); - - memcpy(mic_hdr->ccmp_pn, seq.ccmp.pn, IEEE80211_CCMP_PN_LEN); + pn64 = atomic64_read(&tx_key->tx_pn); + mic_hdr->ccmp_pn[5] = pn64; + mic_hdr->ccmp_pn[4] = pn64 >> 8; + mic_hdr->ccmp_pn[3] = pn64 >> 16; + mic_hdr->ccmp_pn[2] = pn64 >> 24; + mic_hdr->ccmp_pn[1] = pn64 >> 32; + mic_hdr->ccmp_pn[0] = pn64 >> 40; if (ieee80211_has_a4(hdr->frame_control)) mic_hdr->hlen = cpu_to_be16(28); diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c index efb54f53b4f9..76378d225b46 100644 --- a/drivers/staging/vt6656/rxtx.c +++ b/drivers/staging/vt6656/rxtx.c @@ -719,7 +719,7 @@ static void vnt_fill_txkey(struct vnt_usb_send_context *tx_context, u16 payload_len, struct vnt_mic_hdr *mic_hdr) { struct ieee80211_hdr *hdr = tx_context->hdr; - struct ieee80211_key_seq seq; + u64 pn64; u8 *iv = ((u8 *)hdr + ieee80211_get_hdrlen_from_skb(skb)); /* strip header and icv len from payload */ @@ -752,9 +752,13 @@ static void vnt_fill_txkey(struct vnt_usb_send_context *tx_context, mic_hdr->payload_len = cpu_to_be16(payload_len); ether_addr_copy(mic_hdr->mic_addr2, hdr->addr2); - ieee80211_get_key_tx_seq(tx_key, &seq); - - memcpy(mic_hdr->ccmp_pn, seq.ccmp.pn, IEEE80211_CCMP_PN_LEN); + pn64 = atomic64_read(&tx_key->tx_pn); + mic_hdr->ccmp_pn[5] = pn64; + mic_hdr->ccmp_pn[4] = pn64 >> 8; + mic_hdr->ccmp_pn[3] = pn64 >> 16; + mic_hdr->ccmp_pn[2] = pn64 >> 24; + mic_hdr->ccmp_pn[1] = pn64 >> 32; + mic_hdr->ccmp_pn[0] = pn64 >> 40; if (ieee80211_has_a4(hdr->frame_control)) mic_hdr->hlen = cpu_to_be16(28); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 15879b49baad..66155d3ad7e6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4463,23 +4463,6 @@ void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, */ u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key_conf *keyconf, u64 pn); -/** - * ieee80211_get_key_tx_seq - get key TX sequence counter - * - * @keyconf: the parameter passed with the set key - * @seq: buffer to receive the sequence data - * - * This function allows a driver to retrieve the current TX IV/PN - * for the given key. It must not be called if IV generation is - * offloaded to the device. - * - * Note that this function may only be called when no TX processing - * can be done concurrently, for example when queues are stopped - * and the stop has been synchronized. - */ -void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, - struct ieee80211_key_seq *seq); - /** * ieee80211_get_key_rx_seq - get key RX sequence counter * @@ -4499,23 +4482,6 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int tid, struct ieee80211_key_seq *seq); -/** - * ieee80211_set_key_tx_seq - set key TX sequence counter - * - * @keyconf: the parameter passed with the set key - * @seq: new sequence data - * - * This function allows a driver to set the current TX IV/PNs for the - * given key. This is useful when resuming from WoWLAN sleep and the - * device may have transmitted frames using the PTK, e.g. replies to - * ARP requests. - * - * Note that this function may only be called when no TX processing - * can be done concurrently. - */ -void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf, - struct ieee80211_key_seq *seq); - /** * ieee80211_set_key_rx_seq - set key RX sequence counter * diff --git a/net/mac80211/key.c b/net/mac80211/key.c index f9c4cb9c6e06..3df7b0392d30 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -932,51 +932,6 @@ void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, } EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify); -void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, - struct ieee80211_key_seq *seq) -{ - struct ieee80211_key *key; - u64 pn64; - - if (WARN_ON(!(keyconf->flags & IEEE80211_KEY_FLAG_GENERATE_IV))) - return; - - key = container_of(keyconf, struct ieee80211_key, conf); - - switch (key->conf.cipher) { - case WLAN_CIPHER_SUITE_TKIP: - pn64 = atomic64_read(&key->conf.tx_pn); - seq->tkip.iv32 = TKIP_PN_TO_IV32(pn64); - seq->tkip.iv16 = TKIP_PN_TO_IV16(pn64); - break; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_CCMP_256: - case WLAN_CIPHER_SUITE_AES_CMAC: - case WLAN_CIPHER_SUITE_BIP_CMAC_256: - BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) != - offsetof(typeof(*seq), aes_cmac)); - case WLAN_CIPHER_SUITE_BIP_GMAC_128: - case WLAN_CIPHER_SUITE_BIP_GMAC_256: - BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) != - offsetof(typeof(*seq), aes_gmac)); - case WLAN_CIPHER_SUITE_GCMP: - case WLAN_CIPHER_SUITE_GCMP_256: - BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) != - offsetof(typeof(*seq), gcmp)); - pn64 = atomic64_read(&key->conf.tx_pn); - seq->ccmp.pn[5] = pn64; - seq->ccmp.pn[4] = pn64 >> 8; - seq->ccmp.pn[3] = pn64 >> 16; - seq->ccmp.pn[2] = pn64 >> 24; - seq->ccmp.pn[1] = pn64 >> 32; - seq->ccmp.pn[0] = pn64 >> 40; - break; - default: - WARN_ON(1); - } -} -EXPORT_SYMBOL(ieee80211_get_key_tx_seq); - void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int tid, struct ieee80211_key_seq *seq) { @@ -1030,48 +985,6 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, } EXPORT_SYMBOL(ieee80211_get_key_rx_seq); -void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf, - struct ieee80211_key_seq *seq) -{ - struct ieee80211_key *key; - u64 pn64; - - key = container_of(keyconf, struct ieee80211_key, conf); - - switch (key->conf.cipher) { - case WLAN_CIPHER_SUITE_TKIP: - pn64 = (u64)seq->tkip.iv16 | ((u64)seq->tkip.iv32 << 16); - atomic64_set(&key->conf.tx_pn, pn64); - break; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_CCMP_256: - case WLAN_CIPHER_SUITE_AES_CMAC: - case WLAN_CIPHER_SUITE_BIP_CMAC_256: - BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) != - offsetof(typeof(*seq), aes_cmac)); - case WLAN_CIPHER_SUITE_BIP_GMAC_128: - case WLAN_CIPHER_SUITE_BIP_GMAC_256: - BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) != - offsetof(typeof(*seq), aes_gmac)); - case WLAN_CIPHER_SUITE_GCMP: - case WLAN_CIPHER_SUITE_GCMP_256: - BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) != - offsetof(typeof(*seq), gcmp)); - pn64 = (u64)seq->ccmp.pn[5] | - ((u64)seq->ccmp.pn[4] << 8) | - ((u64)seq->ccmp.pn[3] << 16) | - ((u64)seq->ccmp.pn[2] << 24) | - ((u64)seq->ccmp.pn[1] << 32) | - ((u64)seq->ccmp.pn[0] << 40); - atomic64_set(&key->conf.tx_pn, pn64); - break; - default: - WARN_ON(1); - break; - } -} -EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq); - void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf, int tid, struct ieee80211_key_seq *seq) { -- cgit From 65554d07adfc22bb9e14f6df8c609a646f869a74 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 16 Feb 2016 12:48:17 +0200 Subject: mac80211: provide interface to driver to set VHT MU-MIMO data Provide an interface to the lower level driver to set the VHT MU-MIMO data. This is needed for example when there is an update of the group data during low power state, where the management frame will not be passed to the host at all. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 15 +++++++++++++++ net/mac80211/vht.c | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 66155d3ad7e6..23f2a5ecf669 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5445,6 +5445,21 @@ ieee80211_vif_type_p2p(struct ieee80211_vif *vif) return ieee80211_iftype_p2p(vif->type, vif->p2p); } +/** + * ieee80211_update_mu_groups - set the VHT MU-MIMO groud data + * + * @vif: the specified virtual interface + * @membership: 64 bits array - a bit is set if station is member of the group + * @position: 2 bits per group id indicating the position in the group + * + * Note: This function assumes that the given vif is valid and the position and + * membership data is of the correct size and are in the same byte order as the + * matching GroupId management frame. + * Calls to this function need to be serialized with RX path. + */ +void ieee80211_update_mu_groups(struct ieee80211_vif *vif, + const u8 *membership, const u8 *position); + void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, int rssi_min_thold, int rssi_max_thold); diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 341d192cea52..f8f161179b5d 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -2,7 +2,7 @@ * VHT handling * * Portions of this file - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -469,6 +469,20 @@ void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); } +void ieee80211_update_mu_groups(struct ieee80211_vif *vif, + const u8 *membership, const u8 *position) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; + + if (WARN_ON_ONCE(!(sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER))) + return; + + memcpy(bss_conf->mu_group.membership, membership, WLAN_MEMBERSHIP_LEN); + memcpy(bss_conf->mu_group.position, position, WLAN_USER_POSITION_LEN); +} +EXPORT_SYMBOL_GPL(ieee80211_update_mu_groups); + void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, u8 opmode, enum ieee80211_band band) -- cgit From b5a33d52595f0cb153f09bf45a5dcd66a7418dbb Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 16 Feb 2016 12:48:18 +0200 Subject: mac80211: move MU_MIMO_OWNER flag to ieee80211_vif Drivers may need to track which vif is using VHT MU-MIMO. Move the flag indicationg the ownership of MU_MIMO to ieee80211_vif. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/ieee80211_i.h | 2 -- net/mac80211/mlme.c | 11 ++++++----- net/mac80211/util.c | 2 +- net/mac80211/vht.c | 7 +++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 23f2a5ecf669..0c09da34b67a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1382,6 +1382,7 @@ enum ieee80211_vif_flags { * @csa_active: marks whether a channel switch is going on. Internally it is * write-protected by sdata_lock and local->mtx so holding either is fine * for read access. + * @mu_mimo_owner: indicates interface owns MU-MIMO capability * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed @@ -1408,6 +1409,7 @@ struct ieee80211_vif { u8 addr[ETH_ALEN]; bool p2p; bool csa_active; + bool mu_mimo_owner; u8 cab_queue; u8 hw_queue[IEEE80211_NUM_ACS]; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a49c10361f1c..1630975c89f1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -716,7 +716,6 @@ struct ieee80211_if_mesh { * back to wireless media and to the local net stack. * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver - * @IEEE80211_SDATA_MU_MIMO_OWNER: indicates interface owns MU-MIMO capability */ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), @@ -724,7 +723,6 @@ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), IEEE80211_SDATA_IN_DRIVER = BIT(5), - IEEE80211_SDATA_MU_MIMO_OWNER = BIT(6), }; /** diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 857089de475f..f41625bcd879 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6,7 +6,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 Intel Deutschland GmbH + * Copyright (C) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -559,7 +559,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *other; list_for_each_entry_rcu(other, &local->interfaces, list) { - if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) { + if (other->vif.mu_mimo_owner) { disable_mu_mimo = true; break; } @@ -567,7 +567,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, if (disable_mu_mimo) cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; else - sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER; + sdata->vif.mu_mimo_owner = true; } mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; @@ -2052,7 +2052,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(sdata->vif.bss_conf.mu_group.position, 0, sizeof(sdata->vif.bss_conf.mu_group.position)); changed |= BSS_CHANGED_MU_GROUPS; - sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER; + sdata->vif.mu_mimo_owner = false; sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; @@ -2509,7 +2509,8 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, eth_zero_addr(sdata->u.mgd.bssid); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); sdata->u.mgd.flags = 0; - sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER; + sdata->vif.mu_mimo_owner = false; + mutex_lock(&sdata->local->mtx); ieee80211_vif_release_channel(sdata); mutex_unlock(&sdata->local->mtx); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f1e5b76eda70..89f71799df84 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1928,7 +1928,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_IDLE | BSS_CHANGED_TXPOWER; - if (sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER) + if (sdata->vif.mu_mimo_owner) changed |= BSS_CHANGED_MU_GROUPS; switch (sdata->vif.type) { diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index f8f161179b5d..89e04d55aa18 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -450,7 +450,7 @@ void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, { struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; - if (!(sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER)) + if (!sdata->vif.mu_mimo_owner) return; if (!memcmp(mgmt->u.action.u.vht_group_notif.position, @@ -472,10 +472,9 @@ void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, void ieee80211_update_mu_groups(struct ieee80211_vif *vif, const u8 *membership, const u8 *position) { - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - if (WARN_ON_ONCE(!(sdata->flags & IEEE80211_SDATA_MU_MIMO_OWNER))) + if (WARN_ON_ONCE(!vif->mu_mimo_owner)) return; memcpy(bss_conf->mu_group.membership, membership, WLAN_MEMBERSHIP_LEN); -- cgit From 0c9ca11b1ae8eb16c1b6bbae91991392d2321372 Mon Sep 17 00:00:00 2001 From: Beni Lev Date: Wed, 17 Feb 2016 20:30:00 +0200 Subject: cfg80211: Add global RRM capability Today, the supplicant will add the RRM capabilities Information Element in the association request only if Quiet period is supported (NL80211_FEATURE_QUIET). Quiet is one of many RRM features, and there are other RRM features that are not related to Quiet (e.g. neighbor report). Therefore, requiring Quiet to enable RRM is too restrictive. Some of the features, like neighbor report, can be supported by user space without any help from the kernel. Hence adding the RRM capabilities IE to association request should be the sole user space's decision. Removing the RRM dependency on Quiet in the driver solves this problem, but using an old driver with a user space tool that would not require Quiet feature would be problematic: the user space would add NL80211_ATTR_USE_RRM in the association request even if the kernel doesn't advertize NL80211_FEATURE_QUIET and the association would be denied by the kernel. This solution adds a global RRM capability, that tells user space that it can request RRM capabilities IE publishment without any specific feature support in the kernel. Signed-off-by: Beni Lev Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 8 ++++++++ net/wireless/nl80211.c | 18 +++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 7758969a2a8e..5a30a7563633 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1727,6 +1727,8 @@ enum nl80211_commands { * underlying device supports these minimal RRM features: * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES, * %NL80211_FEATURE_QUIET, + * Or, if global RRM is supported, see: + * %NL80211_EXT_FEATURE_RRM * If this flag is used, driver must add the Power Capabilities IE to the * association request. In addition, it must also set the RRM capability * flag in the association request's Capability Info field. @@ -4402,12 +4404,18 @@ enum nl80211_feature_flags { /** * enum nl80211_ext_feature_index - bit index of extended features. * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates. + * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can + * can request to use RRM (see %NL80211_ATTR_USE_RRM) with + * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set + * the ASSOC_REQ_USE_RRM flag in the association request even if + * NL80211_FEATURE_QUIET is not advertized. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VHT_IBSS, + NL80211_EXT_FEATURE_RRM, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 268cb493f6a5..90890f183c0e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3,7 +3,7 @@ * * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright 2015 Intel Deutschland GmbH + * Copyright 2015-2016 Intel Deutschland GmbH */ #include @@ -7286,9 +7286,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) { - if (!(rdev->wiphy.features & - NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) || - !(rdev->wiphy.features & NL80211_FEATURE_QUIET)) + if (!((rdev->wiphy.features & + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) && + (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_RRM)) return -EINVAL; req.flags |= ASSOC_REQ_USE_RRM; } @@ -7976,9 +7978,11 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) { - if (!(rdev->wiphy.features & - NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) || - !(rdev->wiphy.features & NL80211_FEATURE_QUIET)) { + if (!((rdev->wiphy.features & + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) && + (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_RRM)) { kzfree(connkeys); return -EINVAL; } -- cgit From 7837a7778268191dae5f6622f2b92b8b37cb8d7f Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 19 Feb 2016 11:43:04 +0100 Subject: cfg80211: add radiotap VHT info to rtap_namespace_sizes Add IEEE80211_RADIOTAP_VHT entry to rtap_namespace_sizes array in order to define alignment and size of VHT info in tx radiotap Signed-off-by: Lorenzo Bianconi Signed-off-by: Johannes Berg --- net/wireless/radiotap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index 722da616438c..6582d155e2fc 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c @@ -43,6 +43,7 @@ static const struct radiotap_align_size rtap_namespace_sizes[] = { [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, + [IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, }, /* * add more here as they are defined in radiotap.h */ -- cgit From 648b50dd6abf8e6e5b589bb8e6873a4596389dbe Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Jan 2016 12:03:46 +0300 Subject: net: rfkill: add rfkill_find_type function Helper for finding the type based on name. Useful if the type needs to be determined based on device property. Signed-off-by: Heikki Krogerus [modify rfkill_types array and BUILD_BUG_ON to not cause errors] Signed-off-by: Johannes Berg --- include/linux/rfkill.h | 15 +++++++++++++ net/rfkill/core.c | 58 ++++++++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 7af625f6d226..e6a0031d1b1f 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -213,6 +213,15 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw); * @rfkill: rfkill struct to query */ bool rfkill_blocked(struct rfkill *rfkill); + +/** + * rfkill_find_type - Helpper for finding rfkill type by name + * @name: the name of the type + * + * Returns enum rfkill_type that conrresponds the name. + */ +enum rfkill_type rfkill_find_type(const char *name); + #else /* !RFKILL */ static inline struct rfkill * __must_check rfkill_alloc(const char *name, @@ -269,6 +278,12 @@ static inline bool rfkill_blocked(struct rfkill *rfkill) { return false; } + +static inline enum rfkill_type rfkill_find_type(const char *name) +{ + return RFKILL_TYPE_ALL; +} + #endif /* RFKILL || RFKILL_MODULE */ diff --git a/net/rfkill/core.c b/net/rfkill/core.c index a805831d5d9b..2a23479a49f2 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -572,6 +572,34 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) } EXPORT_SYMBOL(rfkill_set_states); +static const char * const rfkill_types[] = { + NULL, /* RFKILL_TYPE_ALL */ + "wlan", + "bluetooth", + "ultrawideband", + "wimax", + "wwan", + "gps", + "fm", + "nfc", +}; + +enum rfkill_type rfkill_find_type(const char *name) +{ + int i; + + BUILD_BUG_ON(ARRAY_SIZE(rfkill_types) != NUM_RFKILL_TYPES); + + if (!name) + return RFKILL_TYPE_ALL; + + for (i = 1; i < NUM_RFKILL_TYPES; i++) + if (!strcmp(name, rfkill_types[i])) + return i; + return RFKILL_TYPE_ALL; +} +EXPORT_SYMBOL(rfkill_find_type); + static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -581,38 +609,12 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(name); -static const char *rfkill_get_type_str(enum rfkill_type type) -{ - BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_NFC + 1); - - switch (type) { - case RFKILL_TYPE_WLAN: - return "wlan"; - case RFKILL_TYPE_BLUETOOTH: - return "bluetooth"; - case RFKILL_TYPE_UWB: - return "ultrawideband"; - case RFKILL_TYPE_WIMAX: - return "wimax"; - case RFKILL_TYPE_WWAN: - return "wwan"; - case RFKILL_TYPE_GPS: - return "gps"; - case RFKILL_TYPE_FM: - return "fm"; - case RFKILL_TYPE_NFC: - return "nfc"; - default: - BUG(); - } -} - static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rfkill *rfkill = to_rfkill(dev); - return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); + return sprintf(buf, "%s\n", rfkill_types[rfkill->type]); } static DEVICE_ATTR_RO(type); @@ -750,7 +752,7 @@ static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) if (error) return error; error = add_uevent_var(env, "RFKILL_TYPE=%s", - rfkill_get_type_str(rfkill->type)); + rfkill_types[rfkill->type]); if (error) return error; spin_lock_irqsave(&rfkill->lock, flags); -- cgit From 7d5e9737efda16535e5b54bd627ef4881d11d31f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Jan 2016 12:03:47 +0300 Subject: net: rfkill: gpio: get the name and type from device property This prepares the driver for removal of platform data. Signed-off-by: Heikki Krogerus Signed-off-by: Johannes Berg --- net/rfkill/rfkill-gpio.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 4b1e3f35f06c..1a9c0316aad1 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -81,7 +81,6 @@ static int rfkill_gpio_acpi_probe(struct device *dev, if (!id) return -ENODEV; - rfkill->name = dev_name(dev); rfkill->type = (unsigned)id->driver_data; return acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), @@ -93,12 +92,21 @@ static int rfkill_gpio_probe(struct platform_device *pdev) struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; struct rfkill_gpio_data *rfkill; struct gpio_desc *gpio; + const char *type_name; int ret; rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL); if (!rfkill) return -ENOMEM; + device_property_read_string(&pdev->dev, "name", &rfkill->name); + device_property_read_string(&pdev->dev, "type", &type_name); + + if (!rfkill->name) + rfkill->name = dev_name(&pdev->dev); + + rfkill->type = rfkill_find_type(type_name); + if (ACPI_HANDLE(&pdev->dev)) { ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill); if (ret) @@ -124,10 +132,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev) rfkill->shutdown_gpio = gpio; - /* Make sure at-least one of the GPIO is defined and that - * a name is specified for this instance - */ - if ((!rfkill->reset_gpio && !rfkill->shutdown_gpio) || !rfkill->name) { + /* Make sure at-least one GPIO is defined for this instance */ + if (!rfkill->reset_gpio && !rfkill->shutdown_gpio) { dev_err(&pdev->dev, "invalid platform data\n"); return -EINVAL; } -- cgit From d54bbaf45af04982d91b424ae0d2e8fe2fb78bd8 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Jan 2016 12:03:48 +0300 Subject: ARM: tegra: use build-in device properties with rfkill_gpio Pass the rfkill name and type to the device with properties instead of driver specific platform data. Signed-off-by: Heikki Krogerus CC: Alexandre Courbot CC: Thierry Reding CC: Stephen Warren Acked-by: Thierry Reding Tested-by: Marc Dietrich Acked-by: Arnd Bergmann Signed-off-by: Johannes Berg --- arch/arm/mach-tegra/board-paz00.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index 49d1110cff53..52db8bf7e153 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -17,23 +17,25 @@ * */ +#include #include #include -#include #include "board.h" -static struct rfkill_gpio_platform_data wifi_rfkill_platform_data = { - .name = "wifi_rfkill", - .type = RFKILL_TYPE_WLAN, +static struct property_entry __initdata wifi_rfkill_prop[] = { + PROPERTY_ENTRY_STRING("name", "wifi_rfkill"), + PROPERTY_ENTRY_STRING("type", "wlan"), + { }, +}; + +static struct property_set __initdata wifi_rfkill_pset = { + .properties = wifi_rfkill_prop, }; static struct platform_device wifi_rfkill_device = { .name = "rfkill_gpio", .id = -1, - .dev = { - .platform_data = &wifi_rfkill_platform_data, - }, }; static struct gpiod_lookup_table wifi_gpio_lookup = { @@ -47,6 +49,7 @@ static struct gpiod_lookup_table wifi_gpio_lookup = { void __init tegra_paz00_wifikill_init(void) { + platform_device_add_properties(&wifi_rfkill_device, &wifi_rfkill_pset); gpiod_add_lookup_table(&wifi_gpio_lookup); platform_device_register(&wifi_rfkill_device); } -- cgit From fb2e6b7b7b02ab35a9d5355a69097a6f60c69d38 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Jan 2016 12:03:49 +0300 Subject: net: rfkill: gpio: remove rfkill_gpio_platform_data No more users for it. Signed-off-by: Heikki Krogerus Signed-off-by: Johannes Berg --- include/linux/rfkill-gpio.h | 37 ------------------------------------- net/rfkill/Kconfig | 3 +-- net/rfkill/rfkill-gpio.c | 8 -------- 3 files changed, 1 insertion(+), 47 deletions(-) delete mode 100644 include/linux/rfkill-gpio.h diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h deleted file mode 100644 index 20bcb55498cd..000000000000 --- a/include/linux/rfkill-gpio.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -#ifndef __RFKILL_GPIO_H -#define __RFKILL_GPIO_H - -#include -#include - -/** - * struct rfkill_gpio_platform_data - platform data for rfkill gpio device. - * for unused gpio's, the expected value is -1. - * @name: name for the gpio rf kill instance - */ - -struct rfkill_gpio_platform_data { - char *name; - enum rfkill_type type; -}; - -#endif /* __RFKILL_GPIO_H */ diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 598d374f6a35..868f1ad0415a 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -41,5 +41,4 @@ config RFKILL_GPIO default n help If you say yes here you get support of a generic gpio RFKILL - driver. The platform should fill in the appropriate fields in the - rfkill_gpio_platform_data structure and pass that to the driver. + driver. diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 1a9c0316aad1..76c01cbd56e3 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -27,8 +27,6 @@ #include #include -#include - struct rfkill_gpio_data { const char *name; enum rfkill_type type; @@ -89,7 +87,6 @@ static int rfkill_gpio_acpi_probe(struct device *dev, static int rfkill_gpio_probe(struct platform_device *pdev) { - struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; struct rfkill_gpio_data *rfkill; struct gpio_desc *gpio; const char *type_name; @@ -111,11 +108,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev) ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill); if (ret) return ret; - } else if (pdata) { - rfkill->name = pdata->name; - rfkill->type = pdata->type; - } else { - return -ENODEV; } rfkill->clk = devm_clk_get(&pdev->dev, NULL); -- cgit From 3ff707d66881d308ef71a0939aa7a92ae1290702 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Mon, 22 Feb 2016 11:36:32 -0500 Subject: rfkill: Improve documentation language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 2a23479a49f2..8f6906eb08f3 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -282,8 +282,8 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked) spin_lock_irqsave(&rfkill->lock, flags); if (err) { /* - * Failed -- reset status to _prev, this may be different - * from what set set _PREV to earlier in this function + * Failed -- reset status to _PREV, which may be different + * from what we have set _PREV to earlier in this function * if rfkill_set_sw_state was invoked. */ if (rfkill->state & RFKILL_BLOCK_SW_PREV) -- cgit From 1a1078901b72e5e8046579cc54250e4f167269d5 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Mon, 22 Feb 2016 11:36:33 -0500 Subject: rfkill: Remove extra blank line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 8f6906eb08f3..f843eeeac46d 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -455,7 +455,6 @@ bool rfkill_get_global_sw_state(const enum rfkill_type type) } #endif - bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) { unsigned long flags; -- cgit From 26b359c85d6ce0960ca122adfa7c1229796f55d7 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Mon, 22 Feb 2016 11:36:34 -0500 Subject: rfkill: Point to the correct deprecated doc location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "claim" sysfs interface has been removed, so its documentation now lives in the "removed" folder. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- Documentation/ABI/stable/sysfs-class-rfkill | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/stable/sysfs-class-rfkill b/Documentation/ABI/stable/sysfs-class-rfkill index 097f522c33bb..e51571eb8b97 100644 --- a/Documentation/ABI/stable/sysfs-class-rfkill +++ b/Documentation/ABI/stable/sysfs-class-rfkill @@ -2,8 +2,10 @@ rfkill - radio frequency (RF) connector kill switch support For details to this subsystem look at Documentation/rfkill.txt. -For the deprecated /sys/class/rfkill/*/state and -/sys/class/rfkill/*/claim knobs of this interface look in +For the deprecated /sys/class/rfkill/*/claim knobs of this interface look in +Documentation/ABI/removed/sysfs-class-rfkill. + +For the deprecated /sys/class/rfkill/*/state knobs of this interface look in Documentation/ABI/obsolete/sysfs-class-rfkill. What: /sys/class/rfkill -- cgit From 4b636dc659ffbca8bc8069dcdf5ae5c2d088a163 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Mon, 22 Feb 2016 11:36:35 -0500 Subject: rfkill: Move "state" sysfs file back to stable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is still quite a bit of code using this interface, so we can't just remove it. Hopefully it will be possible in the future, but since its scheduled removal date is past 2 years already, we are better having the documentation reflecting the current state of things. Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- Documentation/ABI/obsolete/sysfs-class-rfkill | 20 -------------------- Documentation/ABI/stable/sysfs-class-rfkill | 25 ++++++++++++++++++++++--- 2 files changed, 22 insertions(+), 23 deletions(-) delete mode 100644 Documentation/ABI/obsolete/sysfs-class-rfkill diff --git a/Documentation/ABI/obsolete/sysfs-class-rfkill b/Documentation/ABI/obsolete/sysfs-class-rfkill deleted file mode 100644 index e736d145085f..000000000000 --- a/Documentation/ABI/obsolete/sysfs-class-rfkill +++ /dev/null @@ -1,20 +0,0 @@ -rfkill - radio frequency (RF) connector kill switch support - -For details to this subsystem look at Documentation/rfkill.txt. - -What: /sys/class/rfkill/rfkill[0-9]+/state -Date: 09-Jul-2007 -KernelVersion v2.6.22 -Contact: linux-wireless@vger.kernel.org -Description: Current state of the transmitter. - This file is deprecated and scheduled to be removed in 2014, - because its not possible to express the 'soft and hard block' - state of the rfkill driver. -Values: A numeric value. - 0: RFKILL_STATE_SOFT_BLOCKED - transmitter is turned off by software - 1: RFKILL_STATE_UNBLOCKED - transmitter is (potentially) active - 2: RFKILL_STATE_HARD_BLOCKED - transmitter is forced off by something outside of - the driver's control. diff --git a/Documentation/ABI/stable/sysfs-class-rfkill b/Documentation/ABI/stable/sysfs-class-rfkill index e51571eb8b97..e1ba4a104753 100644 --- a/Documentation/ABI/stable/sysfs-class-rfkill +++ b/Documentation/ABI/stable/sysfs-class-rfkill @@ -5,9 +5,6 @@ For details to this subsystem look at Documentation/rfkill.txt. For the deprecated /sys/class/rfkill/*/claim knobs of this interface look in Documentation/ABI/removed/sysfs-class-rfkill. -For the deprecated /sys/class/rfkill/*/state knobs of this interface look in -Documentation/ABI/obsolete/sysfs-class-rfkill. - What: /sys/class/rfkill Date: 09-Jul-2007 KernelVersion: v2.6.22 @@ -44,6 +41,28 @@ Values: A numeric value. 1: true +What: /sys/class/rfkill/rfkill[0-9]+/state +Date: 09-Jul-2007 +KernelVersion v2.6.22 +Contact: linux-wireless@vger.kernel.org +Description: Current state of the transmitter. + This file was scheduled to be removed in 2014, but due to its + large number of users it will be sticking around for a bit + longer. Despite it being marked as stabe, the newer "hard" and + "soft" interfaces should be preffered, since it is not possible + to express the 'soft and hard block' state of the rfkill driver + through this interface. There will likely be another attempt to + remove it in the future. +Values: A numeric value. + 0: RFKILL_STATE_SOFT_BLOCKED + transmitter is turned off by software + 1: RFKILL_STATE_UNBLOCKED + transmitter is (potentially) active + 2: RFKILL_STATE_HARD_BLOCKED + transmitter is forced off by something outside of + the driver's control. + + What: /sys/class/rfkill/rfkill[0-9]+/hard Date: 12-March-2010 KernelVersion v2.6.34 -- cgit From 9487bd6b96a98ee07661a7b5e6f6afce67e2860b Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Mon, 22 Feb 2016 11:36:36 -0500 Subject: rfkill: Factor rfkill_global_states[].cur assignments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor all assignments to rfkill_global_states[].cur into a single function rfkill_update_global_state(). Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- net/rfkill/core.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index f843eeeac46d..a8c05e18da58 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -302,6 +302,19 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked) rfkill_event(rfkill); } +static void rfkill_update_global_state(enum rfkill_type type, bool blocked) +{ + int i; + + if (type != RFKILL_TYPE_ALL) { + rfkill_global_states[type].cur = blocked; + return; + } + + for (i = 0; i < NUM_RFKILL_TYPES; i++) + rfkill_global_states[i].cur = blocked; +} + #ifdef CONFIG_RFKILL_INPUT static atomic_t rfkill_input_disabled = ATOMIC_INIT(0); @@ -319,15 +332,7 @@ static void __rfkill_switch_all(const enum rfkill_type type, bool blocked) { struct rfkill *rfkill; - if (type == RFKILL_TYPE_ALL) { - int i; - - for (i = 0; i < NUM_RFKILL_TYPES; i++) - rfkill_global_states[i].cur = blocked; - } else { - rfkill_global_states[type].cur = blocked; - } - + rfkill_update_global_state(type, blocked); list_for_each_entry(rfkill, &rfkill_list, node) { if (rfkill->type != type && type != RFKILL_TYPE_ALL) continue; @@ -1166,15 +1171,8 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, mutex_lock(&rfkill_global_mutex); - if (ev.op == RFKILL_OP_CHANGE_ALL) { - if (ev.type == RFKILL_TYPE_ALL) { - enum rfkill_type i; - for (i = 0; i < NUM_RFKILL_TYPES; i++) - rfkill_global_states[i].cur = ev.soft; - } else { - rfkill_global_states[ev.type].cur = ev.soft; - } - } + if (ev.op == RFKILL_OP_CHANGE_ALL) + rfkill_update_global_state(ev.type, ev.soft); list_for_each_entry(rfkill, &rfkill_list, node) { if (rfkill->idx != ev.idx && ev.op != RFKILL_OP_CHANGE_ALL) @@ -1263,10 +1261,8 @@ static struct miscdevice rfkill_miscdev = { static int __init rfkill_init(void) { int error; - int i; - for (i = 0; i < NUM_RFKILL_TYPES; i++) - rfkill_global_states[i].cur = !rfkill_default_state; + rfkill_update_global_state(RFKILL_TYPE_ALL, !rfkill_default_state); error = class_register(&rfkill_class); if (error) -- cgit From 50ee738d7271fe825e4024cdfa5c5301a871e2c2 Mon Sep 17 00:00:00 2001 From: João Paulo Rechi Vita Date: Mon, 22 Feb 2016 11:36:37 -0500 Subject: rfkill: Add documentation about LED triggers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Paulo Rechi Vita Signed-off-by: Johannes Berg --- Documentation/rfkill.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index 2ee6ef9a6554..1f0c27049340 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -83,6 +83,8 @@ rfkill drivers that control devices that can be hard-blocked unless they also assign the poll_hw_block() callback (then the rfkill core will poll the device). Don't do this unless you cannot get the event in any other way. +RFKill provides per-switch LED triggers, which can be used to drive LEDs +according to the switch state (LED_FULL when blocked, LED_OFF otherwise). 5. Userspace support -- cgit