For the 4.6 cycle, we have a number of changes:

* Bob's mesh mode rhashtable conversion, this includes
    the rhashtable API change for allocation flags
  * BSSID scan, connect() command reassoc support (Jouni)
  * fast (optimised data only) and support for RSS in mac80211 (myself)
  * various smaller changes
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCgAGBQJXBQ4GAAoJEGt7eEactAAdWiMP/ibaP3I79NDc0s7wCDA+KRkm
 hx0Qx4a0wwm7lDFlnGBjY6yKr+XFDliCvdGX7XGpLSsTioNg7eXPpwx5FQoj6RiV
 8+5RKE9fTguN9ofUzqAwHd9sVOaxvdlXbKfb/N93Gzjpw/meYk58wXdF7Almkroa
 ukgJeMzIlIh+6D96zFEA+Ofzp5chwh+x2Dn0wXutEe9P9fOERA859veAvx65b+Ql
 IRGTqyuY5B/wcbkr4o+DWQwgrdt7Vop9nYVPNWtMHm2JTzfuCSaQ2cD9TnVAK/bg
 /vtqC46KKNLyBRGexAPqdftY9PWcfipgE+n7k+Et4iGSmNm7Z3dEyewgXmqli7XJ
 X8Uiaq+N6Fpe06DVSU7aSRt8NLV64A44jXSfKRI9U2POUqKMn/PMdm8bhPW8qCdM
 ra6myWpQGHWK9e0TQQdShq0NQKGxCZAiSRiiIrbbvXl1CwXxkPCG39wAC3Sh1tEN
 ou4lGraeywGnTjaq+mwLEtHLoug8Y2x+Fz+Ze4Cu2enXxna9lp4lr+rFlc+2+0Er
 o9oPxkTk8krZGIj9M6PNc5W+InMwchaFX3076n67hnFHzFRlOQzkfffbPYlhKJDQ
 f8c9JiNZIoX/fD1TAKsrdO1+EKm/xo7w7pLgbMwQal8Jr88SkITDg0i3oXc56vNQ
 ZK2gUzwvrD/jh0AUyDfN
 =sj7y
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-davem-2016-04-06' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg says:

====================
For the 4.7 cycle, we have a number of changes:
 * Bob's mesh mode rhashtable conversion, this includes
   the rhashtable API change for allocation flags
 * BSSID scan, connect() command reassoc support (Jouni)
 * fast (optimised data only) and support for RSS in mac80211 (myself)
 * various smaller changes
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-04-08 16:42:31 -04:00
commit 1089ac6977
55 changed files with 2036 additions and 1012 deletions

View file

@ -37,14 +37,27 @@ radiotap headers and used to control injection:
HT rate for the transmission (only for devices without own rate control). HT rate for the transmission (only for devices without own rate control).
Also some flags are parsed Also some flags are parsed
IEEE80211_TX_RC_SHORT_GI: use short guard interval IEEE80211_RADIOTAP_MCS_SGI: use short guard interval
IEEE80211_TX_RC_40_MHZ_WIDTH: send in HT40 mode IEEE80211_RADIOTAP_MCS_BW_40: send in HT40 mode
* IEEE80211_RADIOTAP_DATA_RETRIES * IEEE80211_RADIOTAP_DATA_RETRIES
number of retries when either IEEE80211_RADIOTAP_RATE or number of retries when either IEEE80211_RADIOTAP_RATE or
IEEE80211_RADIOTAP_MCS was used IEEE80211_RADIOTAP_MCS was used
* IEEE80211_RADIOTAP_VHT
VHT mcs and number of streams used in the transmission (only for devices
without own rate control). Also other fields are parsed
flags field
IEEE80211_RADIOTAP_VHT_FLAG_SGI: use short guard interval
bandwidth field
1: send using 40MHz channel width
4: send using 80MHz channel width
11: send using 160MHz channel width
The injection code can also skip all other currently defined radiotap fields The injection code can also skip all other currently defined radiotap fields
facilitating replay of captured radiotap headers directly. facilitating replay of captured radiotap headers directly.

View file

@ -979,7 +979,7 @@ static void ath10k_process_rx(struct ath10k *ar,
*status = *rx_status; *status = *rx_status;
ath10k_dbg(ar, ATH10K_DBG_DATA, ath10k_dbg(ar, ATH10K_DBG_DATA,
"rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", "rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%llx fcs-err %i mic-err %i amsdu-more %i\n",
skb, skb,
skb->len, skb->len,
ieee80211_get_SA(hdr), ieee80211_get_SA(hdr),

View file

@ -57,7 +57,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_STRIPPED |
RX_FLAG_DECRYPTED; RX_FLAG_DECRYPTED;
wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag); wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%llx\n", status.flag);
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));

View file

@ -686,7 +686,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx_napi(priv->hw, skb, priv->napi); ieee80211_rx_napi(priv->hw, NULL, skb, priv->napi);
} }
static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)

View file

@ -1499,5 +1499,5 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
/* pass it as regular rx to mac80211 */ /* pass it as regular rx to mac80211 */
ieee80211_rx_napi(mvm->hw, skb, NULL); ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
} }

View file

@ -131,7 +131,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
fraglen, rxb->truesize); fraglen, rxb->truesize);
} }
ieee80211_rx_napi(mvm->hw, skb, napi); ieee80211_rx_napi(mvm->hw, NULL, skb, napi);
} }
/* /*

View file

@ -210,7 +210,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
if (iwl_mvm_check_pn(mvm, skb, queue, sta)) if (iwl_mvm_check_pn(mvm, skb, queue, sta))
kfree_skb(skb); kfree_skb(skb);
else else
ieee80211_rx_napi(mvm->hw, skb, napi); ieee80211_rx_napi(mvm->hw, NULL, skb, napi);
} }
static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,

View file

@ -1909,6 +1909,7 @@ static void hw_scan_work(struct work_struct *work)
/* send probes */ /* send probes */
for (i = 0; i < req->n_ssids; i++) { for (i = 0; i < req->n_ssids; i++) {
struct sk_buff *probe; struct sk_buff *probe;
struct ieee80211_mgmt *mgmt;
probe = ieee80211_probereq_get(hwsim->hw, probe = ieee80211_probereq_get(hwsim->hw,
hwsim->scan_addr, hwsim->scan_addr,
@ -1918,6 +1919,10 @@ static void hw_scan_work(struct work_struct *work)
if (!probe) if (!probe)
continue; continue;
mgmt = (struct ieee80211_mgmt *) probe->data;
memcpy(mgmt->da, req->bssid, ETH_ALEN);
memcpy(mgmt->bssid, req->bssid, ETH_ALEN);
if (req->ie_len) if (req->ie_len)
memcpy(skb_put(probe, req->ie_len), req->ie, memcpy(skb_put(probe, req->ie_len), req->ie,
req->ie_len); req->ie_len);

View file

@ -1913,7 +1913,7 @@ static int gfs2_glocks_open(struct inode *inode, struct file *file)
if (seq->buf) if (seq->buf)
seq->size = GFS2_SEQ_GOODSIZE; seq->size = GFS2_SEQ_GOODSIZE;
gi->gl = NULL; gi->gl = NULL;
ret = rhashtable_walk_init(&gl_hash_table, &gi->hti); ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
} }
return ret; return ret;
} }
@ -1941,7 +1941,7 @@ static int gfs2_glstats_open(struct inode *inode, struct file *file)
if (seq->buf) if (seq->buf)
seq->size = GFS2_SEQ_GOODSIZE; seq->size = GFS2_SEQ_GOODSIZE;
gi->gl = NULL; gi->gl = NULL;
ret = rhashtable_walk_init(&gl_hash_table, &gi->hti); ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
} }
return ret; return ret;
} }

View file

@ -7,6 +7,7 @@
* Copyright (c) 2005, Devicescape Software, Inc. * Copyright (c) 2005, Devicescape Software, Inc.
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 Intel Deutschland GmbH
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
@ -163,6 +164,9 @@ 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 */ /* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */
#define IEEE80211_MAX_FRAME_LEN 2352 #define IEEE80211_MAX_FRAME_LEN 2352
/* Maximal size of an A-MSDU that can be transported in a HT BA session */
#define IEEE80211_MAX_MPDU_LEN_HT_BA 4095
/* Maximal size of an A-MSDU */ /* Maximal size of an A-MSDU */
#define IEEE80211_MAX_MPDU_LEN_HT_3839 3839 #define IEEE80211_MAX_MPDU_LEN_HT_3839 3839
#define IEEE80211_MAX_MPDU_LEN_HT_7935 7935 #define IEEE80211_MAX_MPDU_LEN_HT_7935 7935
@ -637,6 +641,16 @@ static inline bool ieee80211_is_first_frag(__le16 seq_ctrl)
return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0; return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0;
} }
/**
* ieee80211_is_frag - check if a frame is a fragment
* @hdr: 802.11 header of the frame
*/
static inline bool ieee80211_is_frag(struct ieee80211_hdr *hdr)
{
return ieee80211_has_morefrags(hdr->frame_control) ||
hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
}
struct ieee80211s_hdr { struct ieee80211s_hdr {
u8 flags; u8 flags;
u8 ttl; u8 ttl;
@ -1011,6 +1025,16 @@ struct ieee80211_mgmt {
u8 tpc_elem_length; u8 tpc_elem_length;
struct ieee80211_tpc_report_ie tpc; struct ieee80211_tpc_report_ie tpc;
} __packed tpc_report; } __packed tpc_report;
struct {
u8 action_code;
u8 dialog_token;
u8 follow_up;
u8 tod[6];
u8 toa[6];
__le16 tod_error;
__le16 toa_error;
u8 variable[0];
} __packed ftm;
} u; } u;
} __packed action; } __packed action;
} u; } u;

View file

@ -346,7 +346,8 @@ struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht,
struct bucket_table *old_tbl); struct bucket_table *old_tbl);
int rhashtable_insert_rehash(struct rhashtable *ht, struct bucket_table *tbl); int rhashtable_insert_rehash(struct rhashtable *ht, struct bucket_table *tbl);
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter); int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter,
gfp_t gfp);
void rhashtable_walk_exit(struct rhashtable_iter *iter); void rhashtable_walk_exit(struct rhashtable_iter *iter);
int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU); int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
void *rhashtable_walk_next(struct rhashtable_iter *iter); void *rhashtable_walk_next(struct rhashtable_iter *iter);

View file

@ -816,6 +816,7 @@ enum station_parameters_apply_mask {
* @supported_oper_classes_len: number of supported operating classes * @supported_oper_classes_len: number of supported operating classes
* @opmode_notif: operating mode field from Operating Mode Notification * @opmode_notif: operating mode field from Operating Mode Notification
* @opmode_notif_used: information if operating mode field is used * @opmode_notif_used: information if operating mode field is used
* @support_p2p_ps: information if station supports P2P PS mechanism
*/ */
struct station_parameters { struct station_parameters {
const u8 *supported_rates; const u8 *supported_rates;
@ -841,6 +842,7 @@ struct station_parameters {
u8 supported_oper_classes_len; u8 supported_oper_classes_len;
u8 opmode_notif; u8 opmode_notif;
bool opmode_notif_used; bool opmode_notif_used;
int support_p2p_ps;
}; };
/** /**
@ -1455,6 +1457,7 @@ struct cfg80211_ssid {
* @mac_addr_mask: MAC address mask used with randomisation, bits that * @mac_addr_mask: MAC address mask used with randomisation, bits that
* are 0 in the mask should be randomised, bits that are 1 should * are 0 in the mask should be randomised, bits that are 1 should
* be taken from the @mac_addr * be taken from the @mac_addr
* @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
*/ */
struct cfg80211_scan_request { struct cfg80211_scan_request {
struct cfg80211_ssid *ssids; struct cfg80211_ssid *ssids;
@ -1471,6 +1474,7 @@ struct cfg80211_scan_request {
u8 mac_addr[ETH_ALEN] __aligned(2); u8 mac_addr[ETH_ALEN] __aligned(2);
u8 mac_addr_mask[ETH_ALEN] __aligned(2); u8 mac_addr_mask[ETH_ALEN] __aligned(2);
u8 bssid[ETH_ALEN] __aligned(2);
/* internal */ /* internal */
struct wiphy *wiphy; struct wiphy *wiphy;
@ -1617,7 +1621,7 @@ struct cfg80211_inform_bss {
}; };
/** /**
* struct cfg80211_bss_ie_data - BSS entry IE data * struct cfg80211_bss_ies - BSS entry IE data
* @tsf: TSF contained in the frame that carried these IEs * @tsf: TSF contained in the frame that carried these IEs
* @rcu_head: internal use, for freeing * @rcu_head: internal use, for freeing
* @len: length of the IEs * @len: length of the IEs
@ -1856,6 +1860,33 @@ struct cfg80211_ibss_params {
struct ieee80211_ht_cap ht_capa_mask; struct ieee80211_ht_cap ht_capa_mask;
}; };
/**
* struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
*
* @band: band of BSS which should match for RSSI level adjustment.
* @delta: value of RSSI level adjustment.
*/
struct cfg80211_bss_select_adjust {
enum ieee80211_band band;
s8 delta;
};
/**
* struct cfg80211_bss_selection - connection parameters for BSS selection.
*
* @behaviour: requested BSS selection behaviour.
* @param: parameters for requestion behaviour.
* @band_pref: preferred band for %NL80211_BSS_SELECT_ATTR_BAND_PREF.
* @adjust: parameters for %NL80211_BSS_SELECT_ATTR_RSSI_ADJUST.
*/
struct cfg80211_bss_selection {
enum nl80211_bss_select_attr behaviour;
union {
enum ieee80211_band band_pref;
struct cfg80211_bss_select_adjust adjust;
} param;
};
/** /**
* struct cfg80211_connect_params - Connection parameters * struct cfg80211_connect_params - Connection parameters
* *
@ -1893,6 +1924,8 @@ struct cfg80211_ibss_params {
* @vht_capa_mask: The bits of vht_capa which are to be used. * @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 * @pbss: if set, connect to a PCP instead of AP. Valid for DMG
* networks. * networks.
* @bss_select: criteria to be used for BSS selection.
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame
*/ */
struct cfg80211_connect_params { struct cfg80211_connect_params {
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
@ -1916,6 +1949,8 @@ struct cfg80211_connect_params {
struct ieee80211_vht_cap vht_capa; struct ieee80211_vht_cap vht_capa;
struct ieee80211_vht_cap vht_capa_mask; struct ieee80211_vht_cap vht_capa_mask;
bool pbss; bool pbss;
struct cfg80211_bss_selection bss_select;
const u8 *prev_bssid;
}; };
/** /**
@ -3184,6 +3219,9 @@ struct wiphy_vendor_command {
* low rssi when a frame is heard on different channel, then it should set * low rssi when a frame is heard on different channel, then it should set
* this variable to the maximal offset for which it can compensate. * this variable to the maximal offset for which it can compensate.
* This value should be set in MHz. * This value should be set in MHz.
* @bss_select_support: bitmask indicating the BSS selection criteria supported
* by the driver in the .connect() callback. The bit position maps to the
* attribute indices defined in &enum nl80211_bss_select_attr.
*/ */
struct wiphy { struct wiphy {
/* assign these fields before you register the wiphy */ /* assign these fields before you register the wiphy */
@ -3306,6 +3344,8 @@ struct wiphy {
u8 max_num_csa_counters; u8 max_num_csa_counters;
u8 max_adj_channel_rssi_comp; u8 max_adj_channel_rssi_comp;
u32 bss_select_support;
char priv[0] __aligned(NETDEV_ALIGN); char priv[0] __aligned(NETDEV_ALIGN);
}; };

View file

@ -291,7 +291,7 @@ struct ieee80211_vif_chanctx_switch {
* @BSS_CHANGED_PS: PS changed for this BSS (STA mode) * @BSS_CHANGED_PS: PS changed for this BSS (STA mode)
* @BSS_CHANGED_TXPOWER: TX power setting changed for this interface * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
* @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS) * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
* changed (currently only in P2P client mode, GO mode will be later) * changed
* @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available: * @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available:
* currently dtim_period only is under consideration. * currently dtim_period only is under consideration.
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed, * @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
@ -526,6 +526,9 @@ struct ieee80211_mu_group_data {
* userspace), whereas TPC is disabled if %txpower_type is set to * userspace), whereas TPC is disabled if %txpower_type is set to
* NL80211_TX_POWER_FIXED (use value configured from userspace) * NL80211_TX_POWER_FIXED (use value configured from userspace)
* @p2p_noa_attr: P2P NoA attribute for P2P powersave * @p2p_noa_attr: P2P NoA attribute for P2P powersave
* @allow_p2p_go_ps: indication for AP or P2P GO interface, whether it's allowed
* to use P2P PS mechanism or not. AP/P2P GO is not allowed to use P2P PS
* if it has associated clients without P2P PS support.
*/ */
struct ieee80211_bss_conf { struct ieee80211_bss_conf {
const u8 *bssid; const u8 *bssid;
@ -563,6 +566,7 @@ struct ieee80211_bss_conf {
int txpower; int txpower;
enum nl80211_tx_power_setting txpower_type; enum nl80211_tx_power_setting txpower_type;
struct ieee80211_p2p_noa_attr p2p_noa_attr; struct ieee80211_p2p_noa_attr p2p_noa_attr;
bool allow_p2p_go_ps;
}; };
/** /**
@ -709,6 +713,7 @@ enum mac80211_tx_info_flags {
* @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
* frame (PS-Poll or uAPSD). * frame (PS-Poll or uAPSD).
* @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
* @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
* *
* These flags are used in tx_info->control.flags. * These flags are used in tx_info->control.flags.
*/ */
@ -716,6 +721,7 @@ enum mac80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0), IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1), IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
IEEE80211_TX_CTRL_RATE_INJECT = BIT(2), IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
IEEE80211_TX_CTRL_AMSDU = BIT(3),
}; };
/* /*
@ -1034,6 +1040,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* on this subframe * on this subframe
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field) * is stored in the @ampdu_delimiter_crc field)
* @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was
* done by the hardware
* @RX_FLAG_LDPC: LDPC was used * @RX_FLAG_LDPC: LDPC was used
* @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without
* processing it in any regular way. * processing it in any regular way.
@ -1091,6 +1099,7 @@ enum mac80211_rx_flags {
RX_FLAG_5MHZ = BIT(29), RX_FLAG_5MHZ = BIT(29),
RX_FLAG_AMSDU_MORE = BIT(30), RX_FLAG_AMSDU_MORE = BIT(30),
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31), RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
RX_FLAG_MIC_STRIPPED = BIT_ULL(32),
}; };
#define RX_FLAG_STBC_SHIFT 26 #define RX_FLAG_STBC_SHIFT 26
@ -1120,6 +1129,8 @@ enum mac80211_rx_vht_flags {
* *
* @mactime: value in microseconds of the 64-bit Time Synchronization Function * @mactime: value in microseconds of the 64-bit Time Synchronization Function
* (TSF) timer when the first data symbol (MPDU) arrived at the hardware. * (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
* @boottime_ns: CLOCK_BOOTTIME timestamp the frame was received at, this is
* needed only for beacons and probe responses that update the scan cache.
* @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use * @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use
* it but can store it and pass it back to the driver for synchronisation * it but can store it and pass it back to the driver for synchronisation
* @band: the active band when this frame was received * @band: the active band when this frame was received
@ -1146,9 +1157,10 @@ enum mac80211_rx_vht_flags {
*/ */
struct ieee80211_rx_status { struct ieee80211_rx_status {
u64 mactime; u64 mactime;
u64 boottime_ns;
u32 device_timestamp; u32 device_timestamp;
u32 ampdu_reference; u32 ampdu_reference;
u32 flag; u64 flag;
u16 freq; u16 freq;
u8 vht_flag; u8 vht_flag;
u8 rate_idx; u8 rate_idx;
@ -1735,6 +1747,8 @@ struct ieee80211_sta_rates {
* size is min(max_amsdu_len, 7935) bytes. * size is min(max_amsdu_len, 7935) bytes.
* Both additional HT limits must be enforced by the low level driver. * 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). * This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2).
* @support_p2p_ps: indicates whether the STA supports P2P PS mechanism or not.
* @max_rc_amsdu_len: Maximum A-MSDU size in bytes recommended by rate control.
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction) * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
*/ */
struct ieee80211_sta { struct ieee80211_sta {
@ -1755,6 +1769,8 @@ struct ieee80211_sta {
bool mfp; bool mfp;
u8 max_amsdu_subframes; u8 max_amsdu_subframes;
u16 max_amsdu_len; u16 max_amsdu_len;
bool support_p2p_ps;
u16 max_rc_amsdu_len;
struct ieee80211_txq *txq[IEEE80211_NUM_TIDS]; struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
@ -1968,6 +1984,18 @@ struct ieee80211_txq {
* order and does not need to manage its own reorder buffer or BA session * order and does not need to manage its own reorder buffer or BA session
* timeout. * timeout.
* *
* @IEEE80211_HW_USES_RSS: The device uses RSS and thus requires parallel RX,
* which implies using per-CPU station statistics.
*
* @IEEE80211_HW_TX_AMSDU: Hardware (or driver) supports software aggregated
* A-MSDU frames. Requires software tx queueing and fast-xmit support.
* When not using minstrel/minstrel_ht rate control, the driver must
* limit the maximum A-MSDU size based on the current tx rate by setting
* max_rc_amsdu_len in struct ieee80211_sta.
*
* @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
* skbs, needed for zero-copy software A-MSDU.
*
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
@ -2005,6 +2033,9 @@ enum ieee80211_hw_flags {
IEEE80211_HW_BEACON_TX_STATUS, IEEE80211_HW_BEACON_TX_STATUS,
IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR, IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
IEEE80211_HW_SUPPORTS_REORDERING_BUFFER, IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
IEEE80211_HW_USES_RSS,
IEEE80211_HW_TX_AMSDU,
IEEE80211_HW_TX_FRAG_LIST,
/* keep last, obviously */ /* keep last, obviously */
NUM_IEEE80211_HW_FLAGS NUM_IEEE80211_HW_FLAGS
@ -2077,6 +2108,9 @@ enum ieee80211_hw_flags {
* size is smaller (an example is LinkSys WRT120N with FW v1.0.07 * size is smaller (an example is LinkSys WRT120N with FW v1.0.07
* build 002 Jun 18 2012). * build 002 Jun 18 2012).
* *
* @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
* of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
*
* @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
* (if %IEEE80211_HW_QUEUE_CONTROL is set) * (if %IEEE80211_HW_QUEUE_CONTROL is set)
* *
@ -2131,6 +2165,7 @@ struct ieee80211_hw {
u8 max_rate_tries; u8 max_rate_tries;
u8 max_rx_aggregation_subframes; u8 max_rx_aggregation_subframes;
u8 max_tx_aggregation_subframes; u8 max_tx_aggregation_subframes;
u8 max_tx_fragments;
u8 offchannel_tx_hw_queue; u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details; u8 radiotap_mcs_details;
u16 radiotap_vht_details; u16 radiotap_vht_details;
@ -3348,6 +3383,10 @@ enum ieee80211_reconfig_type {
* the function call. * the function call.
* *
* @wake_tx_queue: Called when new packets have been added to the queue. * @wake_tx_queue: Called when new packets have been added to the queue.
* @sync_rx_queues: Process all pending frames in RSS queues. This is a
* synchronization which is needed in case driver has in its RSS queues
* pending frames that were received prior to the control path action
* currently taken (e.g. disassociation) but are not processed yet.
*/ */
struct ieee80211_ops { struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw, void (*tx)(struct ieee80211_hw *hw,
@ -3585,6 +3624,7 @@ struct ieee80211_ops {
void (*wake_tx_queue)(struct ieee80211_hw *hw, void (*wake_tx_queue)(struct ieee80211_hw *hw,
struct ieee80211_txq *txq); struct ieee80211_txq *txq);
void (*sync_rx_queues)(struct ieee80211_hw *hw);
}; };
/** /**
@ -3838,11 +3878,12 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
* This function must be called with BHs disabled. * This function must be called with BHs disabled.
* *
* @hw: the hardware this frame came in on * @hw: the hardware this frame came in on
* @sta: the station the frame was received from, or %NULL
* @skb: the buffer to receive, owned by mac80211 after this call * @skb: the buffer to receive, owned by mac80211 after this call
* @napi: the NAPI context * @napi: the NAPI context
*/ */
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb, void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
struct napi_struct *napi); struct sk_buff *skb, struct napi_struct *napi);
/** /**
* ieee80211_rx - receive frame * ieee80211_rx - receive frame
@ -3866,7 +3907,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
*/ */
static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
{ {
ieee80211_rx_napi(hw, skb, NULL); ieee80211_rx_napi(hw, NULL, skb, NULL);
} }
/** /**

View file

@ -322,7 +322,9 @@
* @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_GET_SCAN: get scan results
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
* %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
* probe requests at CCK rate or not. * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to
* specify a BSSID to scan for; if not included, the wildcard BSSID will
* be used.
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
* NL80211_CMD_GET_SCAN and on the "scan" multicast group) * NL80211_CMD_GET_SCAN and on the "scan" multicast group)
* @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
@ -1795,6 +1797,15 @@ enum nl80211_commands {
* in a PBSS. Specified in %NL80211_CMD_CONNECT to request * in a PBSS. Specified in %NL80211_CMD_CONNECT to request
* connecting to a PCP, and in %NL80211_CMD_START_AP to start * connecting to a PCP, and in %NL80211_CMD_START_AP to start
* a PCP instead of AP. Relevant for DMG networks only. * a PCP instead of AP. Relevant for DMG networks only.
* @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
* BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
* attributes according &enum nl80211_bss_select_attr to indicate what
* BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
* it contains the behaviour-specific attribute containing the parameters for
* BSS selection to be done by driver and/or firmware.
*
* @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
* or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
* *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
@ -2172,6 +2183,10 @@ enum nl80211_attrs {
NL80211_ATTR_PBSS, NL80211_ATTR_PBSS,
NL80211_ATTR_BSS_SELECT,
NL80211_ATTR_STA_SUPPORT_P2P_PS,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -2315,6 +2330,20 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
}; };
/**
* enum nl80211_sta_p2p_ps_status - station support of P2P PS
*
* @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
* @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
* @NUM_NL80211_P2P_PS_STATUS: number of values
*/
enum nl80211_sta_p2p_ps_status {
NL80211_P2P_PS_UNSUPPORTED = 0,
NL80211_P2P_PS_SUPPORTED,
NUM_NL80211_P2P_PS_STATUS,
};
#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER #define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER
/** /**
@ -4665,4 +4694,48 @@ enum nl80211_sched_scan_plan {
__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1 __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
}; };
/**
* struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
*
* @band: band of BSS that must match for RSSI value adjustment.
* @delta: value used to adjust the RSSI value of matching BSS.
*/
struct nl80211_bss_select_rssi_adjust {
__u8 band;
__s8 delta;
} __attribute__((packed));
/**
* enum nl80211_bss_select_attr - attributes for bss selection.
*
* @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
* @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
* is requested.
* @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
* selection should be done such that the specified band is preferred.
* When there are multiple BSS-es in the preferred band, the driver
* shall use RSSI-based BSS selection as a second step. The value of
* this attribute is according to &enum nl80211_band (u32).
* @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
* BSS-es in the specified band is to be adjusted before doing
* RSSI-based BSS selection. The attribute value is a packed structure
* value as specified by &struct nl80211_bss_select_rssi_adjust.
* @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
* @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
*
* One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
* for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
* which the driver shall use.
*/
enum nl80211_bss_select_attr {
__NL80211_BSS_SELECT_ATTR_INVALID,
NL80211_BSS_SELECT_ATTR_RSSI,
NL80211_BSS_SELECT_ATTR_BAND_PREF,
NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
/* keep last */
__NL80211_BSS_SELECT_ATTR_AFTER_LAST,
NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
};
#endif /* __LINUX_NL80211_H */ #endif /* __LINUX_NL80211_H */

View file

@ -487,6 +487,7 @@ EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
* rhashtable_walk_init - Initialise an iterator * rhashtable_walk_init - Initialise an iterator
* @ht: Table to walk over * @ht: Table to walk over
* @iter: Hash table Iterator * @iter: Hash table Iterator
* @gfp: GFP flags for allocations
* *
* This function prepares a hash table walk. * This function prepares a hash table walk.
* *
@ -504,14 +505,15 @@ EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
* You must call rhashtable_walk_exit if this function returns * You must call rhashtable_walk_exit if this function returns
* successfully. * successfully.
*/ */
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter) int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter,
gfp_t gfp)
{ {
iter->ht = ht; iter->ht = ht;
iter->p = NULL; iter->p = NULL;
iter->slot = 0; iter->slot = 0;
iter->skip = 0; iter->skip = 0;
iter->walker = kmalloc(sizeof(*iter->walker), GFP_KERNEL); iter->walker = kmalloc(sizeof(*iter->walker), gfp);
if (!iter->walker) if (!iter->walker)
return -ENOMEM; return -ENOMEM;

View file

@ -143,7 +143,7 @@ static void test_bucket_stats(struct rhashtable *ht)
struct rhashtable_iter hti; struct rhashtable_iter hti;
struct rhash_head *pos; struct rhash_head *pos;
err = rhashtable_walk_init(ht, &hti); err = rhashtable_walk_init(ht, &hti, GFP_KERNEL);
if (err) { if (err) {
pr_warn("Test failed: allocation error"); pr_warn("Test failed: allocation error");
return; return;

View file

@ -501,7 +501,8 @@ static int ila_nl_dump_start(struct netlink_callback *cb)
struct ila_net *ilan = net_generic(net, ila_net_id); struct ila_net *ilan = net_generic(net, ila_net_id);
struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args; struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter); return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter,
GFP_KERNEL);
} }
static int ila_nl_dump_done(struct netlink_callback *cb) static int ila_nl_dump_done(struct netlink_callback *cb)

View file

@ -935,6 +935,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
size_t len) size_t len)
{ {
struct tid_ampdu_tx *tid_tx; struct tid_ampdu_tx *tid_tx;
struct ieee80211_txq *txq;
u16 capab, tid; u16 capab, tid;
u8 buf_size; u8 buf_size;
bool amsdu; bool amsdu;
@ -945,6 +946,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes); buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
txq = sta->sta.txq[tid];
if (!amsdu && txq)
set_bit(IEEE80211_TXQ_NO_AMSDU, &to_txq_info(txq)->flags);
mutex_lock(&sta->ampdu_mlme.mtx); mutex_lock(&sta->ampdu_mlme.mtx);
tid_tx = rcu_dereference_protected_tid_tx(sta, tid); tid_tx = rcu_dereference_protected_tid_tx(sta, tid);

View file

@ -65,11 +65,13 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
return ret; return ret;
if (type == NL80211_IFTYPE_AP_VLAN && if (type == NL80211_IFTYPE_AP_VLAN &&
params && params->use_4addr == 0) params && params->use_4addr == 0) {
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
else if (type == NL80211_IFTYPE_STATION && ieee80211_check_fast_rx_iface(sdata);
params && params->use_4addr >= 0) } else if (type == NL80211_IFTYPE_STATION &&
params && params->use_4addr >= 0) {
sdata->u.mgd.use_4addr = params->use_4addr; sdata->u.mgd.use_4addr = params->use_4addr;
}
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
@ -732,6 +734,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->vif.bss_conf.beacon_int = params->beacon_interval;
sdata->vif.bss_conf.dtim_period = params->dtim_period; sdata->vif.bss_conf.dtim_period = params->dtim_period;
sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.enable_beacon = true;
sdata->vif.bss_conf.allow_p2p_go_ps = sdata->vif.p2p;
sdata->vif.bss_conf.ssid_len = params->ssid_len; sdata->vif.bss_conf.ssid_len = params->ssid_len;
if (params->ssid_len) if (params->ssid_len)
@ -1202,6 +1205,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
params->opmode_notif, band); params->opmode_notif, band);
} }
if (params->support_p2p_ps >= 0)
sta->sta.support_p2p_ps = params->support_p2p_ps;
if (ieee80211_vif_is_mesh(&sdata->vif)) if (ieee80211_vif_is_mesh(&sdata->vif))
sta_apply_mesh_params(local, sta, params); sta_apply_mesh_params(local, sta, params);
@ -1363,6 +1369,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
rcu_assign_pointer(vlansdata->u.vlan.sta, sta); rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
new_4addr = true; new_4addr = true;
__ieee80211_check_fast_rx_iface(vlansdata);
} }
if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
@ -1499,7 +1506,7 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
memset(pinfo, 0, sizeof(*pinfo)); memset(pinfo, 0, sizeof(*pinfo));
pinfo->generation = mesh_paths_generation; pinfo->generation = mpath->sdata->u.mesh.mesh_paths_generation;
pinfo->filled = MPATH_INFO_FRAME_QLEN | pinfo->filled = MPATH_INFO_FRAME_QLEN |
MPATH_INFO_SN | MPATH_INFO_SN |
@ -1577,7 +1584,7 @@ static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
memset(pinfo, 0, sizeof(*pinfo)); memset(pinfo, 0, sizeof(*pinfo));
memcpy(mpp, mpath->mpp, ETH_ALEN); memcpy(mpp, mpath->mpp, ETH_ALEN);
pinfo->generation = mpp_paths_generation; pinfo->generation = mpath->sdata->u.mesh.mpp_paths_generation;
} }
static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
@ -1885,6 +1892,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS; sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
else else
sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS; sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
ieee80211_check_fast_rx_iface(sdata);
} }
if (params->ht_opmode >= 0) { if (params->ht_opmode >= 0) {

View file

@ -127,6 +127,9 @@ static const char *hw_flag_names[] = {
FLAG(BEACON_TX_STATUS), FLAG(BEACON_TX_STATUS),
FLAG(NEEDS_UNIQUE_STA_ADDR), FLAG(NEEDS_UNIQUE_STA_ADDR),
FLAG(SUPPORTS_REORDERING_BUFFER), FLAG(SUPPORTS_REORDERING_BUFFER),
FLAG(USES_RSS),
FLAG(TX_AMSDU),
FLAG(TX_FRAG_LIST),
#undef FLAG #undef FLAG
}; };

View file

@ -3,6 +3,7 @@
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * 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 * 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 * it under the terms of the GNU General Public License version 2 as
@ -51,31 +52,54 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_FILE(aid, sta.aid, D); STA_FILE(aid, sta.aid, D);
static const char * const sta_flag_names[] = {
#define FLAG(F) [WLAN_STA_##F] = #F
FLAG(AUTH),
FLAG(ASSOC),
FLAG(PS_STA),
FLAG(AUTHORIZED),
FLAG(SHORT_PREAMBLE),
FLAG(WDS),
FLAG(CLEAR_PS_FILT),
FLAG(MFP),
FLAG(BLOCK_BA),
FLAG(PS_DRIVER),
FLAG(PSPOLL),
FLAG(TDLS_PEER),
FLAG(TDLS_PEER_AUTH),
FLAG(TDLS_INITIATOR),
FLAG(TDLS_CHAN_SWITCH),
FLAG(TDLS_OFF_CHANNEL),
FLAG(TDLS_WIDER_BW),
FLAG(UAPSD),
FLAG(SP),
FLAG(4ADDR_EVENT),
FLAG(INSERTED),
FLAG(RATE_CONTROL),
FLAG(TOFFSET_KNOWN),
FLAG(MPSP_OWNER),
FLAG(MPSP_RECIPIENT),
FLAG(PS_DELIVER),
#undef FLAG
};
static ssize_t sta_flags_read(struct file *file, char __user *userbuf, static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char buf[121]; char buf[16 * NUM_WLAN_STA_FLAGS], *pos = buf;
char *end = buf + sizeof(buf) - 1;
struct sta_info *sta = file->private_data; struct sta_info *sta = file->private_data;
unsigned int flg;
#define TEST(flg) \ BUILD_BUG_ON(ARRAY_SIZE(sta_flag_names) != NUM_WLAN_STA_FLAGS);
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf), for (flg = 0; flg < NUM_WLAN_STA_FLAGS; flg++) {
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", if (test_sta_flag(sta, flg))
TEST(AUTH), TEST(ASSOC), TEST(PS_STA), pos += scnprintf(pos, end - pos, "%s\n",
TEST(PS_DRIVER), TEST(AUTHORIZED), sta_flag_names[flg]);
TEST(SHORT_PREAMBLE), }
sta->sta.wme ? "WME\n" : "",
TEST(WDS), TEST(CLEAR_PS_FILT), return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
TEST(4ADDR_EVENT), TEST(INSERTED),
TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
} }
STA_OPS(flags); STA_OPS(flags);
@ -151,11 +175,12 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf, static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char _buf[12] = {}, *buf = _buf; char _buf[25] = {}, *buf = _buf;
struct sta_info *sta = file->private_data; struct sta_info *sta = file->private_data;
bool start, tx; bool start, tx;
unsigned long tid; unsigned long tid;
int ret; char *pos;
int ret, timeout = 5000;
if (count > sizeof(_buf)) if (count > sizeof(_buf))
return -EINVAL; return -EINVAL;
@ -164,37 +189,48 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
return -EFAULT; return -EFAULT;
buf[sizeof(_buf) - 1] = '\0'; buf[sizeof(_buf) - 1] = '\0';
pos = buf;
if (strncmp(buf, "tx ", 3) == 0) { buf = strsep(&pos, " ");
buf += 3; if (!buf)
tx = true;
} else if (strncmp(buf, "rx ", 3) == 0) {
buf += 3;
tx = false;
} else
return -EINVAL; return -EINVAL;
if (strncmp(buf, "start ", 6) == 0) { if (!strcmp(buf, "tx"))
buf += 6; tx = true;
else if (!strcmp(buf, "rx"))
tx = false;
else
return -EINVAL;
buf = strsep(&pos, " ");
if (!buf)
return -EINVAL;
if (!strcmp(buf, "start")) {
start = true; start = true;
if (!tx) if (!tx)
return -EINVAL; return -EINVAL;
} else if (strncmp(buf, "stop ", 5) == 0) { } else if (!strcmp(buf, "stop")) {
buf += 5;
start = false; start = false;
} else } else {
return -EINVAL; return -EINVAL;
}
buf = strsep(&pos, " ");
if (!buf)
return -EINVAL;
if (sscanf(buf, "timeout=%d", &timeout) == 1) {
buf = strsep(&pos, " ");
if (!buf || !tx || !start)
return -EINVAL;
}
ret = kstrtoul(buf, 0, &tid); ret = kstrtoul(buf, 0, &tid);
if (ret) if (ret || tid >= IEEE80211_NUM_TIDS)
return ret;
if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL; return -EINVAL;
if (tx) { if (tx) {
if (start) if (start)
ret = ieee80211_start_tx_ba_session(&sta->sta, tid, 5000); ret = ieee80211_start_tx_ba_session(&sta->sta, tid,
timeout);
else else
ret = ieee80211_stop_tx_ba_session(&sta->sta, tid); ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);
} else { } else {
@ -322,14 +358,14 @@ STA_OPS(vht_capa);
#define DEBUGFS_ADD(name) \ #define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \ debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops); sta->debugfs_dir, sta, &sta_ ##name## _ops);
#define DEBUGFS_ADD_COUNTER(name, field) \ #define DEBUGFS_ADD_COUNTER(name, field) \
if (sizeof(sta->field) == sizeof(u32)) \ if (sizeof(sta->field) == sizeof(u32)) \
debugfs_create_u32(#name, 0400, sta->debugfs.dir, \ debugfs_create_u32(#name, 0400, sta->debugfs_dir, \
(u32 *) &sta->field); \ (u32 *) &sta->field); \
else \ else \
debugfs_create_u64(#name, 0400, sta->debugfs.dir, \ debugfs_create_u64(#name, 0400, sta->debugfs_dir, \
(u64 *) &sta->field); (u64 *) &sta->field);
void ieee80211_sta_debugfs_add(struct sta_info *sta) void ieee80211_sta_debugfs_add(struct sta_info *sta)
@ -339,8 +375,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations; struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
u8 mac[3*ETH_ALEN]; u8 mac[3*ETH_ALEN];
sta->debugfs.add_has_run = true;
if (!stations_dir) if (!stations_dir)
return; return;
@ -355,8 +389,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
* destroyed quickly enough the old station's debugfs * destroyed quickly enough the old station's debugfs
* dir might still be around. * dir might still be around.
*/ */
sta->debugfs.dir = debugfs_create_dir(mac, stations_dir); sta->debugfs_dir = debugfs_create_dir(mac, stations_dir);
if (!sta->debugfs.dir) if (!sta->debugfs_dir)
return; return;
DEBUGFS_ADD(flags); DEBUGFS_ADD(flags);
@ -372,14 +406,14 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
debugfs_create_x32("driver_buffered_tids", 0400, debugfs_create_x32("driver_buffered_tids", 0400,
sta->debugfs.dir, sta->debugfs_dir,
(u32 *)&sta->driver_buffered_tids); (u32 *)&sta->driver_buffered_tids);
else else
debugfs_create_x64("driver_buffered_tids", 0400, debugfs_create_x64("driver_buffered_tids", 0400,
sta->debugfs.dir, sta->debugfs_dir,
(u64 *)&sta->driver_buffered_tids); (u64 *)&sta->driver_buffered_tids);
drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir); drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
} }
void ieee80211_sta_debugfs_remove(struct sta_info *sta) void ieee80211_sta_debugfs_remove(struct sta_info *sta)
@ -387,7 +421,7 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta)
struct ieee80211_local *local = sta->local; struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir); drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
debugfs_remove_recursive(sta->debugfs.dir); debugfs_remove_recursive(sta->debugfs_dir);
sta->debugfs.dir = NULL; sta->debugfs_dir = NULL;
} }

View file

@ -1,3 +1,8 @@
/*
* Portions of this file
* Copyright(c) 2016 Intel Deutschland GmbH
*/
#ifndef __MAC80211_DRIVER_OPS #ifndef __MAC80211_DRIVER_OPS
#define __MAC80211_DRIVER_OPS #define __MAC80211_DRIVER_OPS
@ -29,6 +34,16 @@ static inline void drv_tx(struct ieee80211_local *local,
local->ops->tx(&local->hw, control, skb); local->ops->tx(&local->hw, control, skb);
} }
static inline void drv_sync_rx_queues(struct ieee80211_local *local,
struct sta_info *sta)
{
if (local->ops->sync_rx_queues) {
trace_drv_sync_rx_queues(local, sta->sdata, &sta->sta);
local->ops->sync_rx_queues(&local->hw);
trace_drv_return_void(local);
}
}
static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata,
u32 sset, u8 *data) u32 sset, u8 *data)
{ {

View file

@ -649,8 +649,6 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
return NULL; return NULL;
} }
sta->rx_stats.last_rx = jiffies;
/* make sure mandatory rates are always added */ /* make sure mandatory rates are always added */
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates | sta->sta.supp_rates[band] = supp_rates |
@ -670,10 +668,11 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) { list_for_each_entry_rcu(sta, &local->sta_list, list) {
unsigned long last_active = ieee80211_sta_last_active(sta);
if (sta->sdata == sdata && if (sta->sdata == sdata &&
time_after(sta->rx_stats.last_rx + time_is_after_jiffies(last_active +
IEEE80211_IBSS_MERGE_INTERVAL, IEEE80211_IBSS_MERGE_INTERVAL)) {
jiffies)) {
active++; active++;
break; break;
} }
@ -1236,8 +1235,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
if (!sta) if (!sta)
return; return;
sta->rx_stats.last_rx = jiffies;
/* make sure mandatory rates are always added */ /* make sure mandatory rates are always added */
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates | sta->sta.supp_rates[band] = supp_rates |
@ -1259,11 +1256,13 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
mutex_lock(&local->sta_mtx); mutex_lock(&local->sta_mtx);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
unsigned long last_active = ieee80211_sta_last_active(sta);
if (sdata != sta->sdata) if (sdata != sta->sdata)
continue; continue;
if (time_after(jiffies, sta->rx_stats.last_rx + exp_time) || if (time_is_before_jiffies(last_active + exp_time) ||
(time_after(jiffies, sta->rx_stats.last_rx + exp_rsn) && (time_is_before_jiffies(last_active + exp_rsn) &&
sta->sta_state != IEEE80211_STA_AUTHORIZED)) { sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n", sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
sta->sta_state != IEEE80211_STA_AUTHORIZED ? sta->sta_state != IEEE80211_STA_AUTHORIZED ?

View file

@ -696,6 +696,11 @@ struct ieee80211_if_mesh {
/* offset from skb->data while building IE */ /* offset from skb->data while building IE */
int meshconf_offset; int meshconf_offset;
struct mesh_table *mesh_paths;
struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
int mesh_paths_generation;
int mpp_paths_generation;
}; };
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
@ -797,6 +802,7 @@ struct mac80211_qos_map {
enum txq_info_flags { enum txq_info_flags {
IEEE80211_TXQ_STOP, IEEE80211_TXQ_STOP,
IEEE80211_TXQ_AMPDU, IEEE80211_TXQ_AMPDU,
IEEE80211_TXQ_NO_AMSDU,
}; };
struct txq_info { struct txq_info {
@ -1489,6 +1495,11 @@ u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local);
int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
u64 *cookie, gfp_t gfp); u64 *cookie, gfp_t gfp);
void ieee80211_check_fast_rx(struct sta_info *sta);
void __ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata);
void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata);
void ieee80211_clear_fast_rx(struct sta_info *sta);
/* STA code */ /* STA code */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,

View file

@ -1093,7 +1093,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
sdata->fragment_next = 0; sdata->fragment_next = 0;
if (ieee80211_vif_is_mesh(&sdata->vif)) if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rmc_free(sdata); ieee80211_mesh_teardown_sdata(sdata);
} }
static void ieee80211_uninit(struct net_device *dev) static void ieee80211_uninit(struct net_device *dev)

View file

@ -338,6 +338,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
} else { } else {
rcu_assign_pointer(sta->gtk[idx], new); rcu_assign_pointer(sta->gtk[idx], new);
} }
ieee80211_check_fast_rx(sta);
} else { } else {
defunikey = old && defunikey = old &&
old == key_mtx_dereference(sdata->local, old == key_mtx_dereference(sdata->local,

View file

@ -558,6 +558,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
if (!ops->set_key) if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN; wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
wiphy->bss_priv_size = sizeof(struct ieee80211_bss); wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
local = wiphy_priv(wiphy); local = wiphy_priv(wiphy);
@ -854,7 +856,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* Only HW csum features are currently compatible with mac80211 */ /* Only HW csum features are currently compatible with mac80211 */
feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA |
NETIF_F_GSO_SOFTWARE; NETIF_F_GSO_SOFTWARE | NETIF_F_RXCSUM;
if (WARN_ON(hw->netdev_features & ~feature_whitelist)) if (WARN_ON(hw->netdev_features & ~feature_whitelist))
return -EINVAL; return -EINVAL;

View file

@ -25,7 +25,6 @@ bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
void ieee80211s_init(void) void ieee80211s_init(void)
{ {
mesh_pathtbl_init();
mesh_allocated = 1; mesh_allocated = 1;
rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry), rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry),
0, 0, NULL); 0, 0, NULL);
@ -35,7 +34,6 @@ void ieee80211s_stop(void)
{ {
if (!mesh_allocated) if (!mesh_allocated)
return; return;
mesh_pathtbl_unregister();
kmem_cache_destroy(rm_cache); kmem_cache_destroy(rm_cache);
} }
@ -176,22 +174,23 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
return -ENOMEM; return -ENOMEM;
sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1; sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
for (i = 0; i < RMC_BUCKETS; i++) for (i = 0; i < RMC_BUCKETS; i++)
INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]); INIT_HLIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
return 0; return 0;
} }
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
{ {
struct mesh_rmc *rmc = sdata->u.mesh.rmc; struct mesh_rmc *rmc = sdata->u.mesh.rmc;
struct rmc_entry *p, *n; struct rmc_entry *p;
struct hlist_node *n;
int i; int i;
if (!sdata->u.mesh.rmc) if (!sdata->u.mesh.rmc)
return; return;
for (i = 0; i < RMC_BUCKETS; i++) { for (i = 0; i < RMC_BUCKETS; i++) {
list_for_each_entry_safe(p, n, &rmc->bucket[i], list) { hlist_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
list_del(&p->list); hlist_del(&p->list);
kmem_cache_free(rm_cache, p); kmem_cache_free(rm_cache, p);
} }
} }
@ -220,16 +219,20 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
u32 seqnum = 0; u32 seqnum = 0;
int entries = 0; int entries = 0;
u8 idx; u8 idx;
struct rmc_entry *p, *n; struct rmc_entry *p;
struct hlist_node *n;
if (!rmc)
return -1;
/* Don't care about endianness since only match matters */ /* Don't care about endianness since only match matters */
memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum)); memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask; idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) { hlist_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
++entries; ++entries;
if (time_after(jiffies, p->exp_time) || if (time_after(jiffies, p->exp_time) ||
entries == RMC_QUEUE_MAX_LEN) { entries == RMC_QUEUE_MAX_LEN) {
list_del(&p->list); hlist_del(&p->list);
kmem_cache_free(rm_cache, p); kmem_cache_free(rm_cache, p);
--entries; --entries;
} else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa)) } else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa))
@ -243,7 +246,7 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
p->seqnum = seqnum; p->seqnum = seqnum;
p->exp_time = jiffies + RMC_TIMEOUT; p->exp_time = jiffies + RMC_TIMEOUT;
memcpy(p->sa, sa, ETH_ALEN); memcpy(p->sa, sa, ETH_ALEN);
list_add(&p->list, &rmc->bucket[idx]); hlist_add_head(&p->list, &rmc->bucket[idx]);
return 0; return 0;
} }
@ -1348,12 +1351,6 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
mesh_path_start_discovery(sdata); mesh_path_start_discovery(sdata);
if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))
mesh_mpath_table_grow();
if (test_and_clear_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags))
mesh_mpp_table_grow();
if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags)) if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags))
ieee80211_mesh_housekeeping(sdata); ieee80211_mesh_housekeeping(sdata);
@ -1388,6 +1385,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
/* Allocate all mesh structures when creating the first mesh interface. */ /* Allocate all mesh structures when creating the first mesh interface. */
if (!mesh_allocated) if (!mesh_allocated)
ieee80211s_init(); ieee80211s_init();
mesh_pathtbl_init(sdata);
setup_timer(&ifmsh->mesh_path_timer, setup_timer(&ifmsh->mesh_path_timer,
ieee80211_mesh_path_timer, ieee80211_mesh_path_timer,
(unsigned long) sdata); (unsigned long) sdata);
@ -1402,3 +1402,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.bssid = zero_addr; sdata->vif.bss_conf.bssid = zero_addr;
} }
void ieee80211_mesh_teardown_sdata(struct ieee80211_sub_if_data *sdata)
{
mesh_rmc_free(sdata);
mesh_pathtbl_unregister(sdata);
}

View file

@ -21,8 +21,6 @@
/** /**
* enum mesh_path_flags - mac80211 mesh path flags * enum mesh_path_flags - mac80211 mesh path flags
* *
*
*
* @MESH_PATH_ACTIVE: the mesh path can be used for forwarding * @MESH_PATH_ACTIVE: the mesh path can be used for forwarding
* @MESH_PATH_RESOLVING: the discovery process is running for this mesh path * @MESH_PATH_RESOLVING: the discovery process is running for this mesh path
* @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence * @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence
@ -32,6 +30,8 @@
* @MESH_PATH_RESOLVED: the mesh path can has been resolved * @MESH_PATH_RESOLVED: the mesh path can has been resolved
* @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination
* already queued up, waiting for the discovery process to start. * already queued up, waiting for the discovery process to start.
* @MESH_PATH_DELETED: the mesh path has been deleted and should no longer
* be used
* *
* MESH_PATH_RESOLVED is used by the mesh path timer to * MESH_PATH_RESOLVED is used by the mesh path timer to
* decide when to stop or cancel the mesh path discovery. * decide when to stop or cancel the mesh path discovery.
@ -43,6 +43,7 @@ enum mesh_path_flags {
MESH_PATH_FIXED = BIT(3), MESH_PATH_FIXED = BIT(3),
MESH_PATH_RESOLVED = BIT(4), MESH_PATH_RESOLVED = BIT(4),
MESH_PATH_REQ_QUEUED = BIT(5), MESH_PATH_REQ_QUEUED = BIT(5),
MESH_PATH_DELETED = BIT(6),
}; };
/** /**
@ -51,10 +52,6 @@ enum mesh_path_flags {
* *
* *
* @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks * @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks
* @MESH_WORK_GROW_MPATH_TABLE: the mesh path table is full and needs
* to grow.
* @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to
* grow
* @MESH_WORK_ROOT: the mesh root station needs to send a frame * @MESH_WORK_ROOT: the mesh root station needs to send a frame
* @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
* mesh nodes * mesh nodes
@ -62,8 +59,6 @@ enum mesh_path_flags {
*/ */
enum mesh_deferred_task_flags { enum mesh_deferred_task_flags {
MESH_WORK_HOUSEKEEPING, MESH_WORK_HOUSEKEEPING,
MESH_WORK_GROW_MPATH_TABLE,
MESH_WORK_GROW_MPP_TABLE,
MESH_WORK_ROOT, MESH_WORK_ROOT,
MESH_WORK_DRIFT_ADJUST, MESH_WORK_DRIFT_ADJUST,
MESH_WORK_MBSS_CHANGED, MESH_WORK_MBSS_CHANGED,
@ -73,12 +68,16 @@ enum mesh_deferred_task_flags {
* struct mesh_path - mac80211 mesh path structure * struct mesh_path - mac80211 mesh path structure
* *
* @dst: mesh path destination mac address * @dst: mesh path destination mac address
* @mpp: mesh proxy mac address
* @rhash: rhashtable list pointer
* @gate_list: list pointer for known gates list
* @sdata: mesh subif * @sdata: mesh subif
* @next_hop: mesh neighbor to which frames for this destination will be * @next_hop: mesh neighbor to which frames for this destination will be
* forwarded * forwarded
* @timer: mesh path discovery timer * @timer: mesh path discovery timer
* @frame_queue: pending queue for frames sent to this destination while the * @frame_queue: pending queue for frames sent to this destination while the
* path is unresolved * path is unresolved
* @rcu: rcu head for freeing mesh path
* @sn: target sequence number * @sn: target sequence number
* @metric: current metric to this destination * @metric: current metric to this destination
* @hop_count: hops to destination * @hop_count: hops to destination
@ -97,14 +96,16 @@ enum mesh_deferred_task_flags {
* @is_gate: the destination station of this path is a mesh gate * @is_gate: the destination station of this path is a mesh gate
* *
* *
* The combination of dst and sdata is unique in the mesh path table. Since the * The dst address is unique in the mesh path table. Since the mesh_path is
* next_hop STA is only protected by RCU as well, deleting the STA must also * protected by RCU, deleting the next_hop STA must remove / substitute the
* remove/substitute the mesh_path structure and wait until that is no longer * mesh_path structure and wait until that is no longer reachable before
* reachable before destroying the STA completely. * destroying the STA completely.
*/ */
struct mesh_path { struct mesh_path {
u8 dst[ETH_ALEN]; u8 dst[ETH_ALEN];
u8 mpp[ETH_ALEN]; /* used for MPP or MAP */ u8 mpp[ETH_ALEN]; /* used for MPP or MAP */
struct rhash_head rhash;
struct hlist_node gate_list;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct sta_info __rcu *next_hop; struct sta_info __rcu *next_hop;
struct timer_list timer; struct timer_list timer;
@ -128,34 +129,17 @@ struct mesh_path {
/** /**
* struct mesh_table * struct mesh_table
* *
* @hash_buckets: array of hash buckets of the table
* @hashwlock: array of locks to protect write operations, one per bucket
* @hash_mask: 2^size_order - 1, used to compute hash idx
* @hash_rnd: random value used for hash computations
* @entries: number of entries in the table
* @free_node: function to free nodes of the table
* @copy_node: function to copy nodes of the table
* @size_order: determines size of the table, there will be 2^size_order hash
* buckets
* @known_gates: list of known mesh gates and their mpaths by the station. The * @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. * gate's mpath may or may not be resolved and active.
* * @gates_lock: protects updates to known_gates
* rcu_head: RCU head to free the table * @rhead: the rhashtable containing struct mesh_paths, keyed by dest addr
* @entries: number of entries in the table
*/ */
struct mesh_table { struct mesh_table {
/* Number of buckets will be 2^N */ struct hlist_head known_gates;
struct hlist_head *hash_buckets;
spinlock_t *hashwlock; /* One per bucket, for add/del */
unsigned int hash_mask; /* (2^size_order) - 1 */
__u32 hash_rnd; /* Used for hash generation */
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
void (*free_node) (struct hlist_node *p, bool free_leafs);
int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
int size_order;
struct hlist_head *known_gates;
spinlock_t gates_lock; spinlock_t gates_lock;
struct rhashtable rhead;
struct rcu_head rcu_head; atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
}; };
/* Recent multicast cache */ /* Recent multicast cache */
@ -170,20 +154,21 @@ struct mesh_table {
* @seqnum: mesh sequence number of the frame * @seqnum: mesh sequence number of the frame
* @exp_time: expiration time of the entry, in jiffies * @exp_time: expiration time of the entry, in jiffies
* @sa: source address of the frame * @sa: source address of the frame
* @list: hashtable list pointer
* *
* The Recent Multicast Cache keeps track of the latest multicast frames that * The Recent Multicast Cache keeps track of the latest multicast frames that
* have been received by a mesh interface and discards received multicast frames * have been received by a mesh interface and discards received multicast frames
* that are found in the cache. * that are found in the cache.
*/ */
struct rmc_entry { struct rmc_entry {
struct list_head list; struct hlist_node list;
u32 seqnum;
unsigned long exp_time; unsigned long exp_time;
u32 seqnum;
u8 sa[ETH_ALEN]; u8 sa[ETH_ALEN];
}; };
struct mesh_rmc { struct mesh_rmc {
struct list_head bucket[RMC_BUCKETS]; struct hlist_head bucket[RMC_BUCKETS];
u32 idx_mask; u32 idx_mask;
}; };
@ -234,6 +219,7 @@ void ieee80211s_init(void);
void ieee80211s_update_metric(struct ieee80211_local *local, void ieee80211s_update_metric(struct ieee80211_local *local,
struct sta_info *sta, struct sk_buff *skb); struct sta_info *sta, struct sk_buff *skb);
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_teardown_sdata(struct ieee80211_sub_if_data *sdata);
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
@ -299,9 +285,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
void mesh_sta_cleanup(struct sta_info *sta); void mesh_sta_cleanup(struct sta_info *sta);
/* Private interfaces */ /* Private interfaces */
/* Mesh tables */
void mesh_mpath_table_grow(void);
void mesh_mpp_table_grow(void);
/* Mesh paths */ /* Mesh paths */
int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
u8 ttl, const u8 *target, u32 target_sn, u8 ttl, const u8 *target, u32 target_sn,
@ -309,8 +292,8 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta); void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
void mesh_path_flush_pending(struct mesh_path *mpath); void mesh_path_flush_pending(struct mesh_path *mpath);
void mesh_path_tx_pending(struct mesh_path *mpath); void mesh_path_tx_pending(struct mesh_path *mpath);
int mesh_pathtbl_init(void); int mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata);
void mesh_pathtbl_unregister(void); void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata);
int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr); int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr);
void mesh_path_timer(unsigned long data); void mesh_path_timer(unsigned long data);
void mesh_path_flush_by_nexthop(struct sta_info *sta); void mesh_path_flush_by_nexthop(struct sta_info *sta);
@ -319,8 +302,6 @@ void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,
void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
extern int mesh_paths_generation;
extern int mpp_paths_generation;
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
static inline static inline

View file

@ -1012,6 +1012,10 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
goto enddiscovery; goto enddiscovery;
spin_lock_bh(&mpath->state_lock); spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_DELETED) {
spin_unlock_bh(&mpath->state_lock);
goto enddiscovery;
}
mpath->flags &= ~MESH_PATH_REQ_QUEUED; mpath->flags &= ~MESH_PATH_REQ_QUEUED;
if (preq_node->flags & PREQ_Q_F_START) { if (preq_node->flags & PREQ_Q_F_START) {
if (mpath->flags & MESH_PATH_RESOLVING) { if (mpath->flags & MESH_PATH_RESOLVING) {

File diff suppressed because it is too large Load diff

View file

@ -61,7 +61,7 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold; s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
return rssi_threshold == 0 || return rssi_threshold == 0 ||
(sta && (sta &&
(s8)-ewma_signal_read(&sta->rx_stats.avg_signal) > (s8)-ewma_signal_read(&sta->rx_stats_avg.signal) >
rssi_threshold); rssi_threshold);
} }
@ -331,7 +331,9 @@ free:
* *
* @sta: mesh peer link to deactivate * @sta: mesh peer link to deactivate
* *
* All mesh paths with this peer as next hop will be flushed * Mesh paths with this peer as next hop should be flushed
* by the caller outside of plink_lock.
*
* Returns beacon changed flag if the beacon content changed. * Returns beacon changed flag if the beacon content changed.
* *
* Locking: the caller must hold sta->mesh->plink_lock * Locking: the caller must hold sta->mesh->plink_lock
@ -346,7 +348,6 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB) if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
changed = mesh_plink_dec_estab_count(sdata); changed = mesh_plink_dec_estab_count(sdata);
sta->mesh->plink_state = NL80211_PLINK_BLOCKED; sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
ieee80211_mps_sta_status_update(sta); ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta, changed |= ieee80211_mps_set_sta_local_pm(sta,
@ -374,6 +375,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
sta->sta.addr, sta->mesh->llid, sta->mesh->plid, sta->sta.addr, sta->mesh->llid, sta->mesh->plid,
sta->mesh->reason); sta->mesh->reason);
spin_unlock_bh(&sta->mesh->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
mesh_path_flush_by_nexthop(sta);
return changed; return changed;
} }
@ -748,6 +750,7 @@ u32 mesh_plink_block(struct sta_info *sta)
changed = __mesh_plink_deactivate(sta); changed = __mesh_plink_deactivate(sta);
sta->mesh->plink_state = NL80211_PLINK_BLOCKED; sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->mesh->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
mesh_path_flush_by_nexthop(sta);
return changed; return changed;
} }
@ -797,6 +800,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
enum ieee80211_self_protected_actioncode action = 0; enum ieee80211_self_protected_actioncode action = 0;
u32 changed = 0; u32 changed = 0;
bool flush = false;
mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
mplstates[sta->mesh->plink_state], mplevents[event]); mplstates[sta->mesh->plink_state], mplevents[event]);
@ -885,6 +889,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
changed |= mesh_set_short_slot_time(sdata); changed |= mesh_set_short_slot_time(sdata);
mesh_plink_close(sdata, sta, event); mesh_plink_close(sdata, sta, event);
action = WLAN_SP_MESH_PEERING_CLOSE; action = WLAN_SP_MESH_PEERING_CLOSE;
flush = true;
break; break;
case OPN_ACPT: case OPN_ACPT:
action = WLAN_SP_MESH_PEERING_CONFIRM; action = WLAN_SP_MESH_PEERING_CONFIRM;
@ -916,6 +921,8 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
break; break;
} }
spin_unlock_bh(&sta->mesh->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
if (flush)
mesh_path_flush_by_nexthop(sta);
if (action) { if (action) {
mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr, mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
sta->mesh->llid, sta->mesh->plid, sta->mesh->llid, sta->mesh->plid,

View file

@ -122,15 +122,16 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (unlikely(!sdata->u.mgd.associated)) if (unlikely(!ifmgd->associated))
return; return;
ifmgd->probe_send_count = 0; if (ifmgd->probe_send_count)
ifmgd->probe_send_count = 0;
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
return; return;
mod_timer(&sdata->u.mgd.conn_mon_timer, mod_timer(&ifmgd->conn_mon_timer,
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
} }
@ -2216,6 +2217,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
const u8 *ssid; const u8 *ssid;
u8 *dst = ifmgd->associated->bssid; u8 *dst = ifmgd->associated->bssid;
u8 unicast_limit = max(1, max_probe_tries - 3); u8 unicast_limit = max(1, max_probe_tries - 3);
struct sta_info *sta;
/* /*
* Try sending broadcast probe requests for the last three * Try sending broadcast probe requests for the last three
@ -2234,6 +2236,14 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
*/ */
ifmgd->probe_send_count++; ifmgd->probe_send_count++;
if (dst) {
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get(sdata, dst);
if (!WARN_ON(!sta))
ieee80211_check_fast_rx(sta);
mutex_unlock(&sdata->local->sta_mtx);
}
if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
ifmgd->nullfunc_failed = false; ifmgd->nullfunc_failed = false;
ieee80211_send_nullfunc(sdata->local, sdata, false); ieee80211_send_nullfunc(sdata->local, sdata, false);

View file

@ -75,8 +75,6 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
if (!sta) if (!sta)
return; return;
sta->rx_stats.last_rx = jiffies;
/* Add only mandatory rates for now */ /* Add only mandatory rates for now */
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = sta->sta.supp_rates[band] =

View file

@ -96,9 +96,9 @@ static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
{ {
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
struct rate_control_ref *ref = sta->rate_ctrl; struct rate_control_ref *ref = sta->rate_ctrl;
if (ref && sta->debugfs.dir && ref->ops->add_sta_debugfs) if (ref && sta->debugfs_dir && ref->ops->add_sta_debugfs)
ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv, ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv,
sta->debugfs.dir); sta->debugfs_dir);
#endif #endif
} }

View file

@ -883,6 +883,59 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
ratetbl->rate[offset].flags = flags; ratetbl->rate[offset].flags = flags;
} }
static inline int
minstrel_ht_get_prob_ewma(struct minstrel_ht_sta *mi, int rate)
{
int group = rate / MCS_GROUP_RATES;
rate %= MCS_GROUP_RATES;
return mi->groups[group].rates[rate].prob_ewma;
}
static int
minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi)
{
int group = mi->max_prob_rate / MCS_GROUP_RATES;
const struct mcs_group *g = &minstrel_mcs_groups[group];
int rate = mi->max_prob_rate % MCS_GROUP_RATES;
/* Disable A-MSDU if max_prob_rate is bad */
if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100))
return 1;
/* If the rate is slower than single-stream MCS1, make A-MSDU limit small */
if (g->duration[rate] > MCS_DURATION(1, 0, 52))
return 500;
/*
* If the rate is slower than single-stream MCS4, limit A-MSDU to usual
* data packet size
*/
if (g->duration[rate] > MCS_DURATION(1, 0, 104))
return 1600;
/*
* If the rate is slower than single-stream MCS7, or if the max throughput
* rate success probability is less than 75%, limit A-MSDU to twice the usual
* data packet size
*/
if (g->duration[rate] > MCS_DURATION(1, 0, 260) ||
(minstrel_ht_get_prob_ewma(mi, mi->max_tp_rate[0]) <
MINSTREL_FRAC(75, 100)))
return 3200;
/*
* HT A-MPDU limits maximum MPDU size under BA agreement to 4095 bytes.
* Since aggregation sessions are started/stopped without txq flush, use
* the limit here to avoid the complexity of having to de-aggregate
* packets in the queue.
*/
if (!mi->sta->vht_cap.vht_supported)
return IEEE80211_MAX_MPDU_LEN_HT_BA;
/* unlimited */
return 0;
}
static void static void
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{ {
@ -907,6 +960,7 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate); minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate);
} }
mi->sta->max_rc_amsdu_len = minstrel_ht_get_max_amsdu_len(mi);
rates->rate[i].idx = -1; rates->rate[i].idx = -1;
rate_control_set_rates(mp->hw, mi->sta, rates); rate_control_set_rates(mp->hw, mi->sta, rates);
} }
@ -924,6 +978,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
struct minstrel_rate_stats *mrs; struct minstrel_rate_stats *mrs;
struct minstrel_mcs_group_data *mg; struct minstrel_mcs_group_data *mg;
unsigned int sample_dur, sample_group, cur_max_tp_streams; unsigned int sample_dur, sample_group, cur_max_tp_streams;
int tp_rate1, tp_rate2;
int sample_idx = 0; int sample_idx = 0;
if (mi->sample_wait > 0) { if (mi->sample_wait > 0) {
@ -945,14 +1000,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
mrs = &mg->rates[sample_idx]; mrs = &mg->rates[sample_idx];
sample_idx += sample_group * MCS_GROUP_RATES; sample_idx += sample_group * MCS_GROUP_RATES;
/* Set tp_rate1, tp_rate2 to the highest / second highest max_tp_rate */
if (minstrel_get_duration(mi->max_tp_rate[0]) >
minstrel_get_duration(mi->max_tp_rate[1])) {
tp_rate1 = mi->max_tp_rate[1];
tp_rate2 = mi->max_tp_rate[0];
} else {
tp_rate1 = mi->max_tp_rate[0];
tp_rate2 = mi->max_tp_rate[1];
}
/* /*
* Sampling might add some overhead (RTS, no aggregation) * Sampling might add some overhead (RTS, no aggregation)
* to the frame. Hence, don't use sampling for the currently * to the frame. Hence, don't use sampling for the highest currently
* used rates. * used highest throughput or probability rate.
*/ */
if (sample_idx == mi->max_tp_rate[0] || if (sample_idx == mi->max_tp_rate[0] || sample_idx == mi->max_prob_rate)
sample_idx == mi->max_tp_rate[1] ||
sample_idx == mi->max_prob_rate)
return -1; return -1;
/* /*
@ -967,10 +1030,10 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
* if the link is working perfectly. * if the link is working perfectly.
*/ */
cur_max_tp_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / cur_max_tp_streams = minstrel_mcs_groups[tp_rate1 /
MCS_GROUP_RATES].streams; MCS_GROUP_RATES].streams;
sample_dur = minstrel_get_duration(sample_idx); sample_dur = minstrel_get_duration(sample_idx);
if (sample_dur >= minstrel_get_duration(mi->max_tp_rate[1]) && if (sample_dur >= minstrel_get_duration(tp_rate2) &&
(cur_max_tp_streams - 1 < (cur_max_tp_streams - 1 <
minstrel_mcs_groups[sample_group].streams || minstrel_mcs_groups[sample_group].streams ||
sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {

View file

@ -722,8 +722,8 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return -1; return -1;
} }
static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs, static int ieee80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
__le16 fc; __le16 fc;
@ -1421,16 +1421,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
sta->rx_stats.last_rx = jiffies; sta->rx_stats.last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control) && if (ieee80211_is_data(hdr->frame_control) &&
!is_multicast_ether_addr(hdr->addr1)) { !is_multicast_ether_addr(hdr->addr1))
sta->rx_stats.last_rate_idx = sta->rx_stats.last_rate =
status->rate_idx; sta_stats_encode_rate(status);
sta->rx_stats.last_rate_flag =
status->flag;
sta->rx_stats.last_rate_vht_flag =
status->vht_flag;
sta->rx_stats.last_rate_vht_nss =
status->vht_nss;
}
} }
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) { } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
sta->rx_stats.last_rx = jiffies; sta->rx_stats.last_rx = jiffies;
@ -1440,22 +1433,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
* match the current local configuration when processed. * match the current local configuration when processed.
*/ */
sta->rx_stats.last_rx = jiffies; sta->rx_stats.last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control)) { if (ieee80211_is_data(hdr->frame_control))
sta->rx_stats.last_rate_idx = status->rate_idx; sta->rx_stats.last_rate = sta_stats_encode_rate(status);
sta->rx_stats.last_rate_flag = status->flag;
sta->rx_stats.last_rate_vht_flag = status->vht_flag;
sta->rx_stats.last_rate_vht_nss = status->vht_nss;
}
} }
if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_sta_rx_notify(rx->sdata, hdr); ieee80211_sta_rx_notify(rx->sdata, hdr);
sta->rx_stats.fragments++; sta->rx_stats.fragments++;
u64_stats_update_begin(&rx->sta->rx_stats.syncp);
sta->rx_stats.bytes += rx->skb->len; sta->rx_stats.bytes += rx->skb->len;
u64_stats_update_end(&rx->sta->rx_stats.syncp);
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
sta->rx_stats.last_signal = status->signal; sta->rx_stats.last_signal = status->signal;
ewma_signal_add(&sta->rx_stats.avg_signal, -status->signal); ewma_signal_add(&sta->rx_stats_avg.signal, -status->signal);
} }
if (status->chains) { if (status->chains) {
@ -1467,7 +1460,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
continue; continue;
sta->rx_stats.chain_signal_last[i] = signal; sta->rx_stats.chain_signal_last[i] = signal;
ewma_signal_add(&sta->rx_stats.chain_signal_avg[i], ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
-signal); -signal);
} }
} }
@ -1586,7 +1579,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) { if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) {
cs = rx->sta->cipher_scheme; cs = rx->sta->cipher_scheme;
keyid = iwl80211_get_cs_keyid(cs, rx->skb); keyid = ieee80211_get_cs_keyid(cs, rx->skb);
if (unlikely(keyid < 0)) if (unlikely(keyid < 0))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
@ -1670,7 +1663,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
hdrlen = ieee80211_hdrlen(fc); hdrlen = ieee80211_hdrlen(fc);
if (cs) { if (cs) {
keyidx = iwl80211_get_cs_keyid(cs, rx->skb); keyidx = ieee80211_get_cs_keyid(cs, rx->skb);
if (unlikely(keyidx < 0)) if (unlikely(keyidx < 0))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
@ -2129,6 +2122,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
ieee80211_rx_stats(dev, skb->len); ieee80211_rx_stats(dev, skb->len);
if (rx->sta) {
/* The seqno index has the same property as needed
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
* for non-QoS-data frames. Here we know it's a data
* frame, so count MSDUs.
*/
u64_stats_update_begin(&rx->sta->rx_stats.syncp);
rx->sta->rx_stats.msdu[rx->seqno_idx]++;
u64_stats_update_end(&rx->sta->rx_stats.syncp);
}
if ((sdata->vif.type == NL80211_IFTYPE_AP || if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
@ -2415,15 +2419,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
if (rx->sta) {
/* The seqno index has the same property as needed
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
* for non-QoS-data frames. Here we know it's a data
* frame, so count MSDUs.
*/
rx->sta->rx_stats.msdu[rx->seqno_idx]++;
}
/* /*
* Send unexpected-4addr-frame event to hostapd. For older versions, * Send unexpected-4addr-frame event to hostapd. For older versions,
* also drop the frame to cooked monitor interfaces. * also drop the frame to cooked monitor interfaces.
@ -2474,14 +2469,14 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
rx->skb->dev = dev; rx->skb->dev = dev;
if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 && if (!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS) &&
local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
!is_multicast_ether_addr( !is_multicast_ether_addr(
((struct ethhdr *)rx->skb->data)->h_dest) && ((struct ethhdr *)rx->skb->data)->h_dest) &&
(!local->scanning && (!local->scanning &&
!test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) { !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)))
mod_timer(&local->dynamic_ps_timer, jiffies + mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
}
ieee80211_deliver_skb(rx); ieee80211_deliver_skb(rx);
@ -3201,7 +3196,7 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
res = rxh(rx); \ res = rxh(rx); \
if (res != RX_CONTINUE) \ if (res != RX_CONTINUE) \
goto rxh_next; \ goto rxh_next; \
} while (0); } while (0)
/* Lock here to avoid hitting all of the data used in the RX /* Lock here to avoid hitting all of the data used in the RX
* path (e.g. key data, station data, ...) concurrently when * path (e.g. key data, station data, ...) concurrently when
@ -3219,30 +3214,30 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
*/ */
rx->skb = skb; rx->skb = skb;
CALL_RXH(ieee80211_rx_h_check_more_data) CALL_RXH(ieee80211_rx_h_check_more_data);
CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll);
CALL_RXH(ieee80211_rx_h_sta_process) CALL_RXH(ieee80211_rx_h_sta_process);
CALL_RXH(ieee80211_rx_h_decrypt) CALL_RXH(ieee80211_rx_h_decrypt);
CALL_RXH(ieee80211_rx_h_defragment) CALL_RXH(ieee80211_rx_h_defragment);
CALL_RXH(ieee80211_rx_h_michael_mic_verify) CALL_RXH(ieee80211_rx_h_michael_mic_verify);
/* must be after MMIC verify so header is counted in MPDU mic */ /* must be after MMIC verify so header is counted in MPDU mic */
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&rx->sdata->vif)) if (ieee80211_vif_is_mesh(&rx->sdata->vif))
CALL_RXH(ieee80211_rx_h_mesh_fwding); CALL_RXH(ieee80211_rx_h_mesh_fwding);
#endif #endif
CALL_RXH(ieee80211_rx_h_amsdu) CALL_RXH(ieee80211_rx_h_amsdu);
CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_data);
/* special treatment -- needs the queue */ /* special treatment -- needs the queue */
res = ieee80211_rx_h_ctrl(rx, frames); res = ieee80211_rx_h_ctrl(rx, frames);
if (res != RX_CONTINUE) if (res != RX_CONTINUE)
goto rxh_next; goto rxh_next;
CALL_RXH(ieee80211_rx_h_mgmt_check) CALL_RXH(ieee80211_rx_h_mgmt_check);
CALL_RXH(ieee80211_rx_h_action) CALL_RXH(ieee80211_rx_h_action);
CALL_RXH(ieee80211_rx_h_userspace_mgmt) CALL_RXH(ieee80211_rx_h_userspace_mgmt);
CALL_RXH(ieee80211_rx_h_action_return) CALL_RXH(ieee80211_rx_h_action_return);
CALL_RXH(ieee80211_rx_h_mgmt) CALL_RXH(ieee80211_rx_h_mgmt);
rxh_next: rxh_next:
ieee80211_rx_handlers_result(rx, res); ieee80211_rx_handlers_result(rx, res);
@ -3265,10 +3260,10 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
res = rxh(rx); \ res = rxh(rx); \
if (res != RX_CONTINUE) \ if (res != RX_CONTINUE) \
goto rxh_next; \ goto rxh_next; \
} while (0); } while (0)
CALL_RXH(ieee80211_rx_h_check_dup) CALL_RXH(ieee80211_rx_h_check_dup);
CALL_RXH(ieee80211_rx_h_check) CALL_RXH(ieee80211_rx_h_check);
ieee80211_rx_reorder_ampdu(rx, &reorder_release); ieee80211_rx_reorder_ampdu(rx, &reorder_release);
@ -3513,6 +3508,351 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
return false; return false;
} }
void ieee80211_check_fast_rx(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ieee80211_key *key;
struct ieee80211_fast_rx fastrx = {
.dev = sdata->dev,
.vif_type = sdata->vif.type,
.control_port_protocol = sdata->control_port_protocol,
}, *old, *new = NULL;
bool assign = false;
/* use sparse to check that we don't return without updating */
__acquire(check_fast_rx);
BUILD_BUG_ON(sizeof(fastrx.rfc1042_hdr) != sizeof(rfc1042_header));
BUILD_BUG_ON(sizeof(fastrx.rfc1042_hdr) != ETH_ALEN);
ether_addr_copy(fastrx.rfc1042_hdr, rfc1042_header);
ether_addr_copy(fastrx.vif_addr, sdata->vif.addr);
fastrx.uses_rss = ieee80211_hw_check(&local->hw, USES_RSS);
/* fast-rx doesn't do reordering */
if (ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION) &&
!ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER))
goto clear;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
/* 4-addr is harder to deal with, later maybe */
if (sdata->u.mgd.use_4addr)
goto clear;
/* software powersave is a huge mess, avoid all of it */
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK))
goto clear;
if (ieee80211_hw_check(&local->hw, SUPPORTS_PS) &&
!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
goto clear;
if (sta->sta.tdls) {
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
fastrx.expected_ds_bits = 0;
} else {
fastrx.sta_notify = sdata->u.mgd.probe_send_count > 0;
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr3);
fastrx.expected_ds_bits =
cpu_to_le16(IEEE80211_FCTL_FROMDS);
}
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_AP:
/* parallel-rx requires this, at least with calls to
* ieee80211_sta_ps_transition()
*/
if (!ieee80211_hw_check(&local->hw, AP_LINK_PS))
goto clear;
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr3);
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
fastrx.expected_ds_bits = cpu_to_le16(IEEE80211_FCTL_TODS);
fastrx.internal_forward =
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
!sdata->u.vlan.sta);
break;
default:
goto clear;
}
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
goto clear;
rcu_read_lock();
key = rcu_dereference(sta->ptk[sta->ptk_idx]);
if (key) {
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_TKIP:
/* we don't want to deal with MMIC in fast-rx */
goto clear_rcu;
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
break;
default:
/* we also don't want to deal with WEP or cipher scheme
* since those require looking up the key idx in the
* frame, rather than assuming the PTK is used
* (we need to revisit this once we implement the real
* PTK index, which is now valid in the spec, but we
* haven't implemented that part yet)
*/
goto clear_rcu;
}
fastrx.key = true;
fastrx.icv_len = key->conf.icv_len;
}
assign = true;
clear_rcu:
rcu_read_unlock();
clear:
__release(check_fast_rx);
if (assign)
new = kmemdup(&fastrx, sizeof(fastrx), GFP_KERNEL);
spin_lock_bh(&sta->lock);
old = rcu_dereference_protected(sta->fast_rx, true);
rcu_assign_pointer(sta->fast_rx, new);
spin_unlock_bh(&sta->lock);
if (old)
kfree_rcu(old, rcu_head);
}
void ieee80211_clear_fast_rx(struct sta_info *sta)
{
struct ieee80211_fast_rx *old;
spin_lock_bh(&sta->lock);
old = rcu_dereference_protected(sta->fast_rx, true);
RCU_INIT_POINTER(sta->fast_rx, NULL);
spin_unlock_bh(&sta->lock);
if (old)
kfree_rcu(old, rcu_head);
}
void __ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
lockdep_assert_held(&local->sta_mtx);
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata &&
(!sta->sdata->bss || sta->sdata->bss != sdata->bss))
continue;
ieee80211_check_fast_rx(sta);
}
}
void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
mutex_lock(&local->sta_mtx);
__ieee80211_check_fast_rx_iface(sdata);
mutex_unlock(&local->sta_mtx);
}
static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
struct ieee80211_fast_rx *fast_rx)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct sta_info *sta = rx->sta;
int orig_len = skb->len;
int snap_offs = ieee80211_hdrlen(hdr->frame_control);
struct {
u8 snap[sizeof(rfc1042_header)];
__be16 proto;
} *payload __aligned(2);
struct {
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
} addrs __aligned(2);
struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
if (fast_rx->uses_rss)
stats = this_cpu_ptr(sta->pcpu_rx_stats);
/* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
* to a common data structure; drivers can implement that per queue
* but we don't have that information in mac80211
*/
if (!(status->flag & RX_FLAG_DUP_VALIDATED))
return false;
#define FAST_RX_CRYPT_FLAGS (RX_FLAG_PN_VALIDATED | RX_FLAG_DECRYPTED)
/* If using encryption, we also need to have:
* - PN_VALIDATED: similar, but the implementation is tricky
* - DECRYPTED: necessary for PN_VALIDATED
*/
if (fast_rx->key &&
(status->flag & FAST_RX_CRYPT_FLAGS) != FAST_RX_CRYPT_FLAGS)
return false;
/* we don't deal with A-MSDU deaggregation here */
if (status->rx_flags & IEEE80211_RX_AMSDU)
return false;
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return false;
if (unlikely(ieee80211_is_frag(hdr)))
return false;
/* Since our interface address cannot be multicast, this
* implicitly also rejects multicast frames without the
* explicit check.
*
* We shouldn't get any *data* frames not addressed to us
* (AP mode will accept multicast *management* frames), but
* punting here will make it go through the full checks in
* ieee80211_accept_frame().
*/
if (!ether_addr_equal(fast_rx->vif_addr, hdr->addr1))
return false;
if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FROMDS |
IEEE80211_FCTL_TODS)) !=
fast_rx->expected_ds_bits)
goto drop;
/* assign the key to drop unencrypted frames (later)
* and strip the IV/MIC if necessary
*/
if (fast_rx->key && !(status->flag & RX_FLAG_IV_STRIPPED)) {
/* GCMP header length is the same */
snap_offs += IEEE80211_CCMP_HDR_LEN;
}
if (!pskb_may_pull(skb, snap_offs + sizeof(*payload)))
goto drop;
payload = (void *)(skb->data + snap_offs);
if (!ether_addr_equal(payload->snap, fast_rx->rfc1042_hdr))
return false;
/* Don't handle these here since they require special code.
* Accept AARP and IPX even though they should come with a
* bridge-tunnel header - but if we get them this way then
* there's little point in discarding them.
*/
if (unlikely(payload->proto == cpu_to_be16(ETH_P_TDLS) ||
payload->proto == fast_rx->control_port_protocol))
return false;
/* after this point, don't punt to the slowpath! */
if (rx->key && !(status->flag & RX_FLAG_MIC_STRIPPED) &&
pskb_trim(skb, skb->len - fast_rx->icv_len))
goto drop;
if (unlikely(fast_rx->sta_notify)) {
ieee80211_sta_rx_notify(rx->sdata, hdr);
fast_rx->sta_notify = false;
}
/* statistics part of ieee80211_rx_h_sta_process() */
stats->last_rx = jiffies;
stats->last_rate = sta_stats_encode_rate(status);
stats->fragments++;
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
stats->last_signal = status->signal;
if (!fast_rx->uses_rss)
ewma_signal_add(&sta->rx_stats_avg.signal,
-status->signal);
}
if (status->chains) {
int i;
stats->chains = status->chains;
for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
int signal = status->chain_signal[i];
if (!(status->chains & BIT(i)))
continue;
stats->chain_signal_last[i] = signal;
if (!fast_rx->uses_rss)
ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
-signal);
}
}
/* end of statistics */
if (rx->key && !ieee80211_has_protected(hdr->frame_control))
goto drop;
/* do the header conversion - first grab the addresses */
ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs);
ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs);
/* remove the SNAP but leave the ethertype */
skb_pull(skb, snap_offs + sizeof(rfc1042_header));
/* push the addresses in front */
memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs));
skb->dev = fast_rx->dev;
ieee80211_rx_stats(fast_rx->dev, skb->len);
/* The seqno index has the same property as needed
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
* for non-QoS-data frames. Here we know it's a data
* frame, so count MSDUs.
*/
u64_stats_update_begin(&stats->syncp);
stats->msdu[rx->seqno_idx]++;
stats->bytes += orig_len;
u64_stats_update_end(&stats->syncp);
if (fast_rx->internal_forward) {
struct sta_info *dsta = sta_info_get(rx->sdata, skb->data);
if (dsta) {
/*
* Send to wireless media and increase priority by 256
* to keep the received priority instead of
* reclassifying the frame (see cfg80211_classify8021d).
*/
skb->priority += 256;
skb->protocol = htons(ETH_P_802_3);
skb_reset_network_header(skb);
skb_reset_mac_header(skb);
dev_queue_xmit(skb);
return true;
}
}
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, fast_rx->dev);
memset(skb->cb, 0, sizeof(skb->cb));
if (rx->napi)
napi_gro_receive(rx->napi, skb);
else
netif_receive_skb(skb);
return true;
drop:
dev_kfree_skb(skb);
stats->dropped++;
return true;
}
/* /*
* This function returns whether or not the SKB * This function returns whether or not the SKB
* was destined for RX processing or not, which, * was destined for RX processing or not, which,
@ -3527,6 +3867,21 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
rx->skb = skb; rx->skb = skb;
/* See if we can do fast-rx; if we have to copy we already lost,
* so punt in that case. We should never have to deliver a data
* frame to multiple interfaces anyway.
*
* We skip the ieee80211_accept_frame() call and do the necessary
* checking inside ieee80211_invoke_fast_rx().
*/
if (consume && rx->sta) {
struct ieee80211_fast_rx *fast_rx;
fast_rx = rcu_dereference(rx->sta->fast_rx);
if (fast_rx && ieee80211_invoke_fast_rx(rx, fast_rx))
return true;
}
if (!ieee80211_accept_frame(rx)) if (!ieee80211_accept_frame(rx))
return false; return false;
@ -3552,6 +3907,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
* be called with rcu_read_lock protection. * be called with rcu_read_lock protection.
*/ */
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta,
struct sk_buff *skb, struct sk_buff *skb,
struct napi_struct *napi) struct napi_struct *napi)
{ {
@ -3561,7 +3917,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
__le16 fc; __le16 fc;
struct ieee80211_rx_data rx; struct ieee80211_rx_data rx;
struct ieee80211_sub_if_data *prev; struct ieee80211_sub_if_data *prev;
struct sta_info *sta, *prev_sta;
struct rhash_head *tmp; struct rhash_head *tmp;
int err = 0; int err = 0;
@ -3597,7 +3952,14 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_is_beacon(hdr->frame_control))) ieee80211_is_beacon(hdr->frame_control)))
ieee80211_scan_rx(local, skb); ieee80211_scan_rx(local, skb);
if (ieee80211_is_data(fc)) { if (pubsta) {
rx.sta = container_of(pubsta, struct sta_info, sta);
rx.sdata = rx.sta->sdata;
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
return;
goto out;
} else if (ieee80211_is_data(fc)) {
struct sta_info *sta, *prev_sta;
const struct bucket_table *tbl; const struct bucket_table *tbl;
prev_sta = NULL; prev_sta = NULL;
@ -3671,8 +4033,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
* This is the receive path handler. It is called by a low level driver when an * This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware. * 802.11 MPDU is received from the hardware.
*/ */
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb, void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
struct napi_struct *napi) struct sk_buff *skb, struct napi_struct *napi)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rate *rate = NULL; struct ieee80211_rate *rate = NULL;
@ -3771,7 +4133,8 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
ieee80211_tpt_led_trig_rx(local, ieee80211_tpt_led_trig_rx(local,
((struct ieee80211_hdr *)skb->data)->frame_control, ((struct ieee80211_hdr *)skb->data)->frame_control,
skb->len); skb->len);
__ieee80211_rx_handle_packet(hw, skb, napi);
__ieee80211_rx_handle_packet(hw, pubsta, skb, napi);
rcu_read_unlock(); rcu_read_unlock();

View file

@ -66,7 +66,9 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
struct ieee80211_bss *bss; struct ieee80211_bss *bss;
int clen, srlen; int clen, srlen;
struct cfg80211_inform_bss bss_meta = {}; struct cfg80211_inform_bss bss_meta = {
.boottime_ns = rx_status->boottime_ns,
};
bool signal_valid; bool signal_valid;
if (ieee80211_hw_check(&local->hw, SIGNAL_DBM)) if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
@ -303,6 +305,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr); ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr);
ether_addr_copy(local->hw_scan_req->req.mac_addr_mask, ether_addr_copy(local->hw_scan_req->req.mac_addr_mask,
req->mac_addr_mask); req->mac_addr_mask);
ether_addr_copy(local->hw_scan_req->req.bssid, req->bssid);
return true; return true;
} }
@ -497,7 +500,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
for (i = 0; i < scan_req->n_ssids; i++) for (i = 0; i < scan_req->n_ssids; i++)
ieee80211_send_probe_req( ieee80211_send_probe_req(
sdata, local->scan_addr, NULL, sdata, local->scan_addr, scan_req->bssid,
scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len, scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
scan_req->ie, scan_req->ie_len, scan_req->ie, scan_req->ie_len,
scan_req->rates[band], false, scan_req->rates[band], false,
@ -562,6 +565,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
req->n_channels * sizeof(req->channels[0]); req->n_channels * sizeof(req->channels[0]);
local->hw_scan_req->req.ie = ies; local->hw_scan_req->req.ie = ies;
local->hw_scan_req->req.flags = req->flags; local->hw_scan_req->req.flags = req->flags;
eth_broadcast_addr(local->hw_scan_req->req.bssid);
local->hw_scan_band = 0; local->hw_scan_band = 0;

View file

@ -2,7 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH * 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 * 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 * it under the terms of the GNU General Public License version 2 as
@ -254,6 +254,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh); kfree(sta->mesh);
#endif #endif
free_percpu(sta->pcpu_rx_stats);
kfree(sta); kfree(sta);
} }
@ -311,6 +312,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
if (!sta) if (!sta)
return NULL; return NULL;
if (ieee80211_hw_check(hw, USES_RSS)) {
sta->pcpu_rx_stats =
alloc_percpu(struct ieee80211_sta_rx_stats);
if (!sta->pcpu_rx_stats)
goto free;
}
spin_lock_init(&sta->lock); spin_lock_init(&sta->lock);
spin_lock_init(&sta->ps_lock); spin_lock_init(&sta->ps_lock);
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames); INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
@ -335,15 +343,17 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta->sdata = sdata; sta->sdata = sdata;
sta->rx_stats.last_rx = jiffies; sta->rx_stats.last_rx = jiffies;
u64_stats_init(&sta->rx_stats.syncp);
sta->sta_state = IEEE80211_STA_NONE; sta->sta_state = IEEE80211_STA_NONE;
/* Mark TID as unreserved */ /* Mark TID as unreserved */
sta->reserved_tid = IEEE80211_TID_UNRESERVED; sta->reserved_tid = IEEE80211_TID_UNRESERVED;
sta->last_connected = ktime_get_seconds(); sta->last_connected = ktime_get_seconds();
ewma_signal_init(&sta->rx_stats.avg_signal); ewma_signal_init(&sta->rx_stats_avg.signal);
for (i = 0; i < ARRAY_SIZE(sta->rx_stats.chain_signal_avg); i++) for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++)
ewma_signal_init(&sta->rx_stats.chain_signal_avg[i]); ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]);
if (local->ops->wake_tx_queue) { if (local->ops->wake_tx_queue) {
void *txq_data; void *txq_data;
@ -406,6 +416,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
} }
} }
sta->sta.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
return sta; return sta;
@ -875,6 +887,13 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
set_sta_flag(sta, WLAN_STA_BLOCK_BA); set_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
/*
* Before removing the station from the driver there might be pending
* rx frames on RSS queues sent prior to the disassociation - wait for
* all such frames to be processed.
*/
drv_sync_rx_queues(local, sta);
ret = sta_info_hash_del(local, sta); ret = sta_info_hash_del(local, sta);
if (WARN_ON(ret)) if (WARN_ON(ret))
return ret; return ret;
@ -1087,10 +1106,12 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->sta_mtx); mutex_lock(&local->sta_mtx);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
unsigned long last_active = ieee80211_sta_last_active(sta);
if (sdata != sta->sdata) if (sdata != sta->sdata)
continue; continue;
if (time_after(jiffies, sta->rx_stats.last_rx + exp_time)) { if (time_is_before_jiffies(last_active + exp_time)) {
sta_dbg(sta->sdata, "expiring inactive STA %pM\n", sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
sta->sta.addr); sta->sta.addr);
@ -1760,6 +1781,31 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
} }
EXPORT_SYMBOL(ieee80211_sta_set_buffered); EXPORT_SYMBOL(ieee80211_sta_set_buffered);
static void
ieee80211_recalc_p2p_go_ps_allowed(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
bool allow_p2p_go_ps = sdata->vif.p2p;
struct sta_info *sta;
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata ||
!test_sta_flag(sta, WLAN_STA_ASSOC))
continue;
if (!sta->sta.support_p2p_ps) {
allow_p2p_go_ps = false;
break;
}
}
rcu_read_unlock();
if (allow_p2p_go_ps != sdata->vif.bss_conf.allow_p2p_go_ps) {
sdata->vif.bss_conf.allow_p2p_go_ps = allow_p2p_go_ps;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_P2P_PS);
}
}
int sta_info_move_state(struct sta_info *sta, int sta_info_move_state(struct sta_info *sta,
enum ieee80211_sta_state new_state) enum ieee80211_sta_state new_state)
{ {
@ -1821,12 +1867,16 @@ int sta_info_move_state(struct sta_info *sta,
} else if (sta->sta_state == IEEE80211_STA_ASSOC) { } else if (sta->sta_state == IEEE80211_STA_ASSOC) {
clear_bit(WLAN_STA_ASSOC, &sta->_flags); clear_bit(WLAN_STA_ASSOC, &sta->_flags);
ieee80211_recalc_min_chandef(sta->sdata); ieee80211_recalc_min_chandef(sta->sdata);
if (!sta->sta.support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
} }
break; break;
case IEEE80211_STA_ASSOC: case IEEE80211_STA_ASSOC:
if (sta->sta_state == IEEE80211_STA_AUTH) { if (sta->sta_state == IEEE80211_STA_AUTH) {
set_bit(WLAN_STA_ASSOC, &sta->_flags); set_bit(WLAN_STA_ASSOC, &sta->_flags);
ieee80211_recalc_min_chandef(sta->sdata); ieee80211_recalc_min_chandef(sta->sdata);
if (!sta->sta.support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
if (sta->sdata->vif.type == NL80211_IFTYPE_AP || if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
(sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
@ -1834,6 +1884,7 @@ int sta_info_move_state(struct sta_info *sta,
atomic_dec(&sta->sdata->bss->num_mcast_sta); atomic_dec(&sta->sdata->bss->num_mcast_sta);
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
ieee80211_clear_fast_xmit(sta); ieee80211_clear_fast_xmit(sta);
ieee80211_clear_fast_rx(sta);
} }
break; break;
case IEEE80211_STA_AUTHORIZED: case IEEE80211_STA_AUTHORIZED:
@ -1844,6 +1895,7 @@ int sta_info_move_state(struct sta_info *sta,
atomic_inc(&sta->sdata->bss->num_mcast_sta); atomic_inc(&sta->sdata->bss->num_mcast_sta);
set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
ieee80211_check_fast_xmit(sta); ieee80211_check_fast_xmit(sta);
ieee80211_check_fast_rx(sta);
} }
break; break;
default: default:
@ -1890,43 +1942,117 @@ u8 sta_info_tx_streams(struct sta_info *sta)
>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
} }
static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) static struct ieee80211_sta_rx_stats *
sta_get_last_rx_stats(struct sta_info *sta)
{ {
rinfo->flags = 0; struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
struct ieee80211_local *local = sta->local;
int cpu;
if (sta->rx_stats.last_rate_flag & RX_FLAG_HT) { if (!ieee80211_hw_check(&local->hw, USES_RSS))
rinfo->flags |= RATE_INFO_FLAGS_MCS; return stats;
rinfo->mcs = sta->rx_stats.last_rate_idx;
} else if (sta->rx_stats.last_rate_flag & RX_FLAG_VHT) { for_each_possible_cpu(cpu) {
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; struct ieee80211_sta_rx_stats *cpustats;
rinfo->nss = sta->rx_stats.last_rate_vht_nss;
rinfo->mcs = sta->rx_stats.last_rate_idx; cpustats = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
} else {
if (time_after(cpustats->last_rx, stats->last_rx))
stats = cpustats;
}
return stats;
}
static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
struct rate_info *rinfo)
{
rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >>
STA_STATS_RATE_BW_SHIFT;
if (rate & STA_STATS_RATE_VHT) {
rinfo->flags = RATE_INFO_FLAGS_VHT_MCS;
rinfo->mcs = rate & 0xf;
rinfo->nss = (rate & 0xf0) >> 4;
} else if (rate & STA_STATS_RATE_HT) {
rinfo->flags = RATE_INFO_FLAGS_MCS;
rinfo->mcs = rate & 0xff;
} else if (rate & STA_STATS_RATE_LEGACY) {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
u16 brate; u16 brate;
unsigned int shift;
sband = sta->local->hw.wiphy->bands[ sband = local->hw.wiphy->bands[(rate >> 4) & 0xf];
ieee80211_get_sdata_band(sta->sdata)]; brate = sband->bitrates[rate & 0xf].bitrate;
brate = sband->bitrates[sta->rx_stats.last_rate_idx].bitrate; if (rinfo->bw == RATE_INFO_BW_5)
shift = 2;
else if (rinfo->bw == RATE_INFO_BW_10)
shift = 1;
else
shift = 0;
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
} }
if (sta->rx_stats.last_rate_flag & RX_FLAG_SHORT_GI) if (rate & STA_STATS_RATE_SGI)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
}
if (sta->rx_stats.last_rate_flag & RX_FLAG_5MHZ) static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
rinfo->bw = RATE_INFO_BW_5; {
else if (sta->rx_stats.last_rate_flag & RX_FLAG_10MHZ) u16 rate = ACCESS_ONCE(sta_get_last_rx_stats(sta)->last_rate);
rinfo->bw = RATE_INFO_BW_10;
else if (sta->rx_stats.last_rate_flag & RX_FLAG_40MHZ) if (rate == STA_STATS_RATE_INVALID)
rinfo->bw = RATE_INFO_BW_40; rinfo->flags = 0;
else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_80MHZ)
rinfo->bw = RATE_INFO_BW_80;
else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_160MHZ)
rinfo->bw = RATE_INFO_BW_160;
else else
rinfo->bw = RATE_INFO_BW_20; sta_stats_decode_rate(sta->local, rate, rinfo);
}
static void sta_set_tidstats(struct sta_info *sta,
struct cfg80211_tid_stats *tidstats,
int tid)
{
struct ieee80211_local *local = sta->local;
if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
unsigned int start;
do {
start = u64_stats_fetch_begin(&sta->rx_stats.syncp);
tidstats->rx_msdu = sta->rx_stats.msdu[tid];
} while (u64_stats_fetch_retry(&sta->rx_stats.syncp, start));
tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
}
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
tidstats->tx_msdu = sta->tx_stats.msdu[tid];
}
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
tidstats->tx_msdu_retries = sta->status_stats.msdu_retries[tid];
}
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
tidstats->tx_msdu_failed = sta->status_stats.msdu_failed[tid];
}
}
static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats)
{
unsigned int start;
u64 value;
do {
start = u64_stats_fetch_begin(&rxstats->syncp);
value = rxstats->bytes;
} while (u64_stats_fetch_retry(&rxstats->syncp, start));
return value;
} }
void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
@ -1935,7 +2061,10 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct rate_control_ref *ref = NULL; struct rate_control_ref *ref = NULL;
u32 thr = 0; u32 thr = 0;
int i, ac; int i, ac, cpu;
struct ieee80211_sta_rx_stats *last_rxstats;
last_rxstats = sta_get_last_rx_stats(sta);
if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
ref = local->rate_ctrl; ref = local->rate_ctrl;
@ -1964,7 +2093,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->connected_time = ktime_get_seconds() - sta->last_connected; sinfo->connected_time = ktime_get_seconds() - sta->last_connected;
sinfo->inactive_time = sinfo->inactive_time =
jiffies_to_msecs(jiffies - sta->rx_stats.last_rx); jiffies_to_msecs(jiffies - ieee80211_sta_last_active(sta));
if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) | if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) |
BIT(NL80211_STA_INFO_TX_BYTES)))) { BIT(NL80211_STA_INFO_TX_BYTES)))) {
@ -1983,12 +2112,30 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) | if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) |
BIT(NL80211_STA_INFO_RX_BYTES)))) { BIT(NL80211_STA_INFO_RX_BYTES)))) {
sinfo->rx_bytes = sta->rx_stats.bytes; sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats);
if (sta->pcpu_rx_stats) {
for_each_possible_cpu(cpu) {
struct ieee80211_sta_rx_stats *cpurxs;
cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
sinfo->rx_bytes += sta_get_stats_bytes(cpurxs);
}
}
sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64); sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
} }
if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_PACKETS))) { if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_PACKETS))) {
sinfo->rx_packets = sta->rx_stats.packets; sinfo->rx_packets = sta->rx_stats.packets;
if (sta->pcpu_rx_stats) {
for_each_possible_cpu(cpu) {
struct ieee80211_sta_rx_stats *cpurxs;
cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
sinfo->rx_packets += cpurxs->packets;
}
}
sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
} }
@ -2003,6 +2150,14 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
} }
sinfo->rx_dropped_misc = sta->rx_stats.dropped; sinfo->rx_dropped_misc = sta->rx_stats.dropped;
if (sta->pcpu_rx_stats) {
for_each_possible_cpu(cpu) {
struct ieee80211_sta_rx_stats *cpurxs;
cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
sinfo->rx_packets += cpurxs->dropped;
}
}
if (sdata->vif.type == NL80211_IFTYPE_STATION && if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) { !(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) {
@ -2014,29 +2169,36 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
if (ieee80211_hw_check(&sta->local->hw, SIGNAL_DBM) || if (ieee80211_hw_check(&sta->local->hw, SIGNAL_DBM) ||
ieee80211_hw_check(&sta->local->hw, SIGNAL_UNSPEC)) { ieee80211_hw_check(&sta->local->hw, SIGNAL_UNSPEC)) {
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) { if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) {
sinfo->signal = (s8)sta->rx_stats.last_signal; sinfo->signal = (s8)last_rxstats->last_signal;
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
} }
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) { if (!sta->pcpu_rx_stats &&
!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
sinfo->signal_avg = sinfo->signal_avg =
-ewma_signal_read(&sta->rx_stats.avg_signal); -ewma_signal_read(&sta->rx_stats_avg.signal);
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG); sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
} }
} }
if (sta->rx_stats.chains && /* for the average - if pcpu_rx_stats isn't set - rxstats must point to
* the sta->rx_stats struct, so the check here is fine with and without
* pcpu statistics
*/
if (last_rxstats->chains &&
!(sinfo->filled & (BIT(NL80211_STA_INFO_CHAIN_SIGNAL) | !(sinfo->filled & (BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)))) { BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)))) {
sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL) | sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); if (!sta->pcpu_rx_stats)
sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
sinfo->chains = last_rxstats->chains;
sinfo->chains = sta->rx_stats.chains;
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
sinfo->chain_signal[i] = sinfo->chain_signal[i] =
sta->rx_stats.chain_signal_last[i]; last_rxstats->chain_signal_last[i];
sinfo->chain_signal_avg[i] = sinfo->chain_signal_avg[i] =
-ewma_signal_read(&sta->rx_stats.chain_signal_avg[i]); -ewma_signal_read(&sta->rx_stats_avg.chain_signal[i]);
} }
} }
@ -2055,33 +2217,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) { sta_set_tidstats(sta, tidstats, i);
tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
tidstats->rx_msdu = sta->rx_stats.msdu[i];
}
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
tidstats->tx_msdu = sta->tx_stats.msdu[i];
}
if (!(tidstats->filled &
BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
tidstats->filled |=
BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
tidstats->tx_msdu_retries =
sta->status_stats.msdu_retries[i];
}
if (!(tidstats->filled &
BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
tidstats->filled |=
BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
tidstats->tx_msdu_failed =
sta->status_stats.msdu_failed[i];
}
} }
if (ieee80211_vif_is_mesh(&sdata->vif)) { if (ieee80211_vif_is_mesh(&sdata->vif)) {
@ -2150,3 +2286,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->expected_throughput = thr; sinfo->expected_throughput = thr;
} }
} }
unsigned long ieee80211_sta_last_active(struct sta_info *sta)
{
struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta);
if (time_after(stats->last_rx, sta->status_stats.last_ack))
return stats->last_rx;
return sta->status_stats.last_ack;
}

View file

@ -1,7 +1,7 @@
/* /*
* Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2013-2014 Intel Mobile Communications GmbH * 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 * 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 * it under the terms of the GNU General Public License version 2 as
@ -18,6 +18,7 @@
#include <linux/average.h> #include <linux/average.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include <linux/u64_stats_sync.h>
#include "key.h" #include "key.h"
/** /**
@ -69,6 +70,8 @@
* @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP. * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
* @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX * @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
* until pending frames are delivered * until pending frames are delivered
*
* @NUM_WLAN_STA_FLAGS: number of defined flags
*/ */
enum ieee80211_sta_info_flags { enum ieee80211_sta_info_flags {
WLAN_STA_AUTH, WLAN_STA_AUTH,
@ -97,6 +100,8 @@ enum ieee80211_sta_info_flags {
WLAN_STA_MPSP_OWNER, WLAN_STA_MPSP_OWNER,
WLAN_STA_MPSP_RECIPIENT, WLAN_STA_MPSP_RECIPIENT,
WLAN_STA_PS_DELIVER, WLAN_STA_PS_DELIVER,
NUM_WLAN_STA_FLAGS,
}; };
#define ADDBA_RESP_INTERVAL HZ #define ADDBA_RESP_INTERVAL HZ
@ -280,6 +285,40 @@ struct ieee80211_fast_tx {
struct rcu_head rcu_head; struct rcu_head rcu_head;
}; };
/**
* struct ieee80211_fast_rx - RX fastpath information
* @dev: netdevice for reporting the SKB
* @vif_type: (P2P-less) interface type of the original sdata (sdata->vif.type)
* @vif_addr: interface address
* @rfc1042_hdr: copy of the RFC 1042 SNAP header (to have in cache)
* @control_port_protocol: control port protocol copied from sdata
* @expected_ds_bits: from/to DS bits expected
* @icv_len: length of the MIC if present
* @key: bool indicating encryption is expected (key is set)
* @sta_notify: notify the MLME code (once)
* @internal_forward: forward froms internally on AP/VLAN type interfaces
* @uses_rss: copy of USES_RSS hw flag
* @da_offs: offset of the DA in the header (for header conversion)
* @sa_offs: offset of the SA in the header (for header conversion)
* @rcu_head: RCU head for freeing this structure
*/
struct ieee80211_fast_rx {
struct net_device *dev;
enum nl80211_iftype vif_type;
u8 vif_addr[ETH_ALEN] __aligned(2);
u8 rfc1042_hdr[6] __aligned(2);
__be16 control_port_protocol;
__le16 expected_ds_bits;
u8 icv_len;
u8 key:1,
sta_notify:1,
internal_forward:1,
uses_rss:1;
u8 da_offs, sa_offs;
struct rcu_head rcu_head;
};
/** /**
* struct mesh_sta - mesh STA information * struct mesh_sta - mesh STA information
* @plink_lock: serialize access to plink fields * @plink_lock: serialize access to plink fields
@ -330,6 +369,21 @@ struct mesh_sta {
DECLARE_EWMA(signal, 1024, 8) DECLARE_EWMA(signal, 1024, 8)
struct ieee80211_sta_rx_stats {
unsigned long packets;
unsigned long last_rx;
unsigned long num_duplicates;
unsigned long fragments;
unsigned long dropped;
int last_signal;
u8 chains;
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
u16 last_rate;
struct u64_stats_sync syncp;
u64 bytes;
u64 msdu[IEEE80211_NUM_TIDS + 1];
};
/** /**
* struct sta_info - STA information * struct sta_info - STA information
* *
@ -371,7 +425,7 @@ DECLARE_EWMA(signal, 1024, 8)
* @ampdu_mlme: A-MPDU state machine state * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers * @timer_to_tid: identity mapping to ID timers
* @mesh: mesh STA information * @mesh: mesh STA information
* @debugfs: debug filesystem info * @debugfs_dir: debug filesystem directory dentry
* @dead: set to true when sta is unlinked * @dead: set to true when sta is unlinked
* @removed: set to true when sta is being removed from sta_list * @removed: set to true when sta is being removed from sta_list
* @uploaded: set to true when sta is uploaded to the driver * @uploaded: set to true when sta is uploaded to the driver
@ -386,10 +440,13 @@ DECLARE_EWMA(signal, 1024, 8)
* @cipher_scheme: optional cipher scheme for this station * @cipher_scheme: optional cipher scheme for this station
* @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED) * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
* @fast_tx: TX fastpath information * @fast_tx: TX fastpath information
* @fast_rx: RX fastpath information
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
* the BSS one. * the BSS one.
* @tx_stats: TX statistics * @tx_stats: TX statistics
* @rx_stats: RX statistics * @rx_stats: RX statistics
* @pcpu_rx_stats: per-CPU RX statistics, assigned only if the driver needs
* this (by advertising the USES_RSS hw flag)
* @status_stats: TX status statistics * @status_stats: TX status statistics
*/ */
struct sta_info { struct sta_info {
@ -409,6 +466,8 @@ struct sta_info {
spinlock_t lock; spinlock_t lock;
struct ieee80211_fast_tx __rcu *fast_tx; struct ieee80211_fast_tx __rcu *fast_tx;
struct ieee80211_fast_rx __rcu *fast_rx;
struct ieee80211_sta_rx_stats __percpu *pcpu_rx_stats;
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
struct mesh_sta *mesh; struct mesh_sta *mesh;
@ -438,24 +497,11 @@ struct sta_info {
long last_connected; long last_connected;
/* Updated from RX path only, no locking requirements */ /* Updated from RX path only, no locking requirements */
struct ieee80211_sta_rx_stats rx_stats;
struct { struct {
unsigned long packets; struct ewma_signal signal;
u64 bytes; struct ewma_signal chain_signal[IEEE80211_MAX_CHAINS];
unsigned long last_rx; } rx_stats_avg;
unsigned long num_duplicates;
unsigned long fragments;
unsigned long dropped;
int last_signal;
struct ewma_signal avg_signal;
u8 chains;
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
int last_rate_idx;
u32 last_rate_flag;
u32 last_rate_vht_flag;
u8 last_rate_vht_nss;
u64 msdu[IEEE80211_NUM_TIDS + 1];
} rx_stats;
/* Plus 1 for non-QoS frames */ /* Plus 1 for non-QoS frames */
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
@ -468,6 +514,7 @@ struct sta_info {
unsigned long last_tdls_pkt_time; unsigned long last_tdls_pkt_time;
u64 msdu_retries[IEEE80211_NUM_TIDS + 1]; u64 msdu_retries[IEEE80211_NUM_TIDS + 1];
u64 msdu_failed[IEEE80211_NUM_TIDS + 1]; u64 msdu_failed[IEEE80211_NUM_TIDS + 1];
unsigned long last_ack;
} status_stats; } status_stats;
/* Updated from TX path only, no locking requirements */ /* Updated from TX path only, no locking requirements */
@ -486,10 +533,7 @@ struct sta_info {
u8 timer_to_tid[IEEE80211_NUM_TIDS]; u8 timer_to_tid[IEEE80211_NUM_TIDS];
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
struct sta_info_debugfsdentries { struct dentry *debugfs_dir;
struct dentry *dir;
bool add_has_run;
} debugfs;
#endif #endif
enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
@ -677,4 +721,44 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
unsigned long ieee80211_sta_last_active(struct sta_info *sta);
#define STA_STATS_RATE_INVALID 0
#define STA_STATS_RATE_VHT 0x8000
#define STA_STATS_RATE_HT 0x4000
#define STA_STATS_RATE_LEGACY 0x2000
#define STA_STATS_RATE_SGI 0x1000
#define STA_STATS_RATE_BW_SHIFT 9
#define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT)
static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s)
{
u16 r = s->rate_idx;
if (s->vht_flag & RX_VHT_FLAG_80MHZ)
r |= RATE_INFO_BW_80 << STA_STATS_RATE_BW_SHIFT;
else if (s->vht_flag & RX_VHT_FLAG_160MHZ)
r |= RATE_INFO_BW_160 << STA_STATS_RATE_BW_SHIFT;
else if (s->flag & RX_FLAG_40MHZ)
r |= RATE_INFO_BW_40 << STA_STATS_RATE_BW_SHIFT;
else if (s->flag & RX_FLAG_10MHZ)
r |= RATE_INFO_BW_10 << STA_STATS_RATE_BW_SHIFT;
else if (s->flag & RX_FLAG_5MHZ)
r |= RATE_INFO_BW_5 << STA_STATS_RATE_BW_SHIFT;
else
r |= RATE_INFO_BW_20 << STA_STATS_RATE_BW_SHIFT;
if (s->flag & RX_FLAG_SHORT_GI)
r |= STA_STATS_RATE_SGI;
if (s->flag & RX_FLAG_VHT)
r |= STA_STATS_RATE_VHT | (s->vht_nss << 4);
else if (s->flag & RX_FLAG_HT)
r |= STA_STATS_RATE_HT;
else
r |= STA_STATS_RATE_LEGACY | (s->band << 4);
return r;
}
#endif /* STA_INFO_H */ #endif /* STA_INFO_H */

View file

@ -188,7 +188,7 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
sta->rx_stats.last_rx = jiffies; sta->status_stats.last_ack = jiffies;
if (ieee80211_is_data_qos(mgmt->frame_control)) { if (ieee80211_is_data_qos(mgmt->frame_control)) {
struct ieee80211_hdr *hdr = (void *) skb->data; struct ieee80211_hdr *hdr = (void *) skb->data;
@ -647,7 +647,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
sta->status_stats.retry_count += retry_count; sta->status_stats.retry_count += retry_count;
if (acked) { if (acked) {
sta->rx_stats.last_rx = jiffies; sta->status_stats.last_ack = jiffies;
if (sta->status_stats.lost_packets) if (sta->status_stats.lost_packets)
sta->status_stats.lost_packets = 0; sta->status_stats.lost_packets = 0;

View file

@ -1,3 +1,8 @@
/*
* Portions of this file
* Copyright(c) 2016 Intel Deutschland GmbH
*/
#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
#define __MAC80211_DRIVER_TRACE #define __MAC80211_DRIVER_TRACE
@ -899,6 +904,13 @@ DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove,
TP_ARGS(local, sdata, sta) TP_ARGS(local, sdata, sta)
); );
DEFINE_EVENT(sta_event, drv_sync_rx_queues,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta),
TP_ARGS(local, sdata, sta)
);
DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update, DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update,
TP_PROTO(struct ieee80211_local *local, TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,

View file

@ -1324,6 +1324,10 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
out: out:
spin_unlock_bh(&txqi->queue.lock); spin_unlock_bh(&txqi->queue.lock);
if (skb && skb_has_frag_list(skb) &&
!ieee80211_hw_check(&local->hw, TX_FRAG_LIST))
skb_linearize(skb);
return skb; return skb;
} }
EXPORT_SYMBOL(ieee80211_tx_dequeue); EXPORT_SYMBOL(ieee80211_tx_dequeue);
@ -1691,7 +1695,9 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
bool rate_found = false; bool rate_found = false;
u8 rate_retries = 0; u8 rate_retries = 0;
u16 rate_flags = 0; u16 rate_flags = 0;
u8 mcs_known, mcs_flags; u8 mcs_known, mcs_flags, mcs_bw;
u16 vht_known;
u8 vht_mcs = 0, vht_nss = 0;
int i; int i;
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
@ -1767,11 +1773,38 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
mcs_flags & IEEE80211_RADIOTAP_MCS_SGI) mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
rate_flags |= IEEE80211_TX_RC_SHORT_GI; rate_flags |= IEEE80211_TX_RC_SHORT_GI;
mcs_bw = mcs_flags & IEEE80211_RADIOTAP_MCS_BW_MASK;
if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW && if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40) mcs_bw == IEEE80211_RADIOTAP_MCS_BW_40)
rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
break; break;
case IEEE80211_RADIOTAP_VHT:
vht_known = get_unaligned_le16(iterator.this_arg);
rate_found = true;
rate_flags = IEEE80211_TX_RC_VHT_MCS;
if ((vht_known & IEEE80211_RADIOTAP_VHT_KNOWN_GI) &&
(iterator.this_arg[2] &
IEEE80211_RADIOTAP_VHT_FLAG_SGI))
rate_flags |= IEEE80211_TX_RC_SHORT_GI;
if (vht_known &
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH) {
if (iterator.this_arg[3] == 1)
rate_flags |=
IEEE80211_TX_RC_40_MHZ_WIDTH;
else if (iterator.this_arg[3] == 4)
rate_flags |=
IEEE80211_TX_RC_80_MHZ_WIDTH;
else if (iterator.this_arg[3] == 11)
rate_flags |=
IEEE80211_TX_RC_160_MHZ_WIDTH;
}
vht_mcs = iterator.this_arg[4] >> 4;
vht_nss = iterator.this_arg[4] & 0xF;
break;
/* /*
* Please update the file * Please update the file
* Documentation/networking/mac80211-injection.txt * Documentation/networking/mac80211-injection.txt
@ -1797,6 +1830,9 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
if (rate_flags & IEEE80211_TX_RC_MCS) { if (rate_flags & IEEE80211_TX_RC_MCS) {
info->control.rates[0].idx = rate; info->control.rates[0].idx = rate;
} else if (rate_flags & IEEE80211_TX_RC_VHT_MCS) {
ieee80211_rate_set_vht(info->control.rates, vht_mcs,
vht_nss);
} else { } else {
for (i = 0; i < sband->n_bitrates; i++) { for (i = 0; i < sband->n_bitrates; i++) {
if (rate * 5 != sband->bitrates[i].bitrate) if (rate * 5 != sband->bitrates[i].bitrate)
@ -1807,6 +1843,9 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
} }
} }
if (info->control.rates[0].idx < 0)
info->control.flags &= ~IEEE80211_TX_CTRL_RATE_INJECT;
info->control.rates[0].flags = rate_flags; info->control.rates[0].flags = rate_flags;
info->control.rates[0].count = min_t(u8, rate_retries + 1, info->control.rates[0].count = min_t(u8, rate_retries + 1,
local->hw.max_rate_tries); local->hw.max_rate_tries);
@ -2181,7 +2220,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
} }
if (mppath && mpath) if (mppath && mpath)
mesh_path_del(mpath->sdata, mpath->dst); mesh_path_del(sdata, mpath->dst);
} }
/* /*
@ -2767,6 +2806,154 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta)
kfree_rcu(fast_tx, rcu_head); kfree_rcu(fast_tx, rcu_head);
} }
static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
struct sk_buff *skb, int headroom,
int *subframe_len)
{
int amsdu_len = *subframe_len + sizeof(struct ethhdr);
int padding = (4 - amsdu_len) & 3;
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
I802_DEBUG_INC(local->tx_expand_skb_head);
if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
wiphy_debug(local->hw.wiphy,
"failed to reallocate TX buffer\n");
return false;
}
}
if (padding) {
*subframe_len += padding;
memset(skb_put(skb, padding), 0, padding);
}
return true;
}
static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
struct ieee80211_fast_tx *fast_tx,
struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr;
struct ethhdr amsdu_hdr;
int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header);
int subframe_len = skb->len - hdr_len;
void *data;
u8 *qc;
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
return false;
if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
return true;
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr),
&subframe_len))
return false;
amsdu_hdr.h_proto = cpu_to_be16(subframe_len);
memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN);
memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN);
data = skb_push(skb, sizeof(amsdu_hdr));
memmove(data, data + sizeof(amsdu_hdr), hdr_len);
memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr));
hdr = data;
qc = ieee80211_get_qos_ctl(hdr);
*qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
info->control.flags |= IEEE80211_TX_CTRL_AMSDU;
return true;
}
static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_fast_tx *fast_tx,
struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
struct ieee80211_txq *txq = sta->sta.txq[tid];
struct txq_info *txqi;
struct sk_buff **frag_tail, *head;
int subframe_len = skb->len - ETH_ALEN;
u8 max_subframes = sta->sta.max_amsdu_subframes;
int max_frags = local->hw.max_tx_fragments;
int max_amsdu_len = sta->sta.max_amsdu_len;
__be16 len;
void *data;
bool ret = false;
int n = 1, nfrags;
if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
return false;
if (!txq)
return false;
txqi = to_txq_info(txq);
if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
return false;
if (sta->sta.max_rc_amsdu_len)
max_amsdu_len = min_t(int, max_amsdu_len,
sta->sta.max_rc_amsdu_len);
spin_lock_bh(&txqi->queue.lock);
head = skb_peek_tail(&txqi->queue);
if (!head)
goto out;
if (skb->len + head->len > max_amsdu_len)
goto out;
if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
goto out;
nfrags = 1 + skb_shinfo(skb)->nr_frags;
nfrags += 1 + skb_shinfo(head)->nr_frags;
frag_tail = &skb_shinfo(head)->frag_list;
while (*frag_tail) {
nfrags += 1 + skb_shinfo(*frag_tail)->nr_frags;
frag_tail = &(*frag_tail)->next;
n++;
}
if (max_subframes && n > max_subframes)
goto out;
if (max_frags && nfrags > max_frags)
goto out;
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
&subframe_len))
goto out;
ret = true;
data = skb_push(skb, ETH_ALEN + 2);
memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
data += 2 * ETH_ALEN;
len = cpu_to_be16(subframe_len);
memcpy(data, &len, 2);
memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
head->len += skb->len;
head->data_len += skb->len;
*frag_tail = skb;
out:
spin_unlock_bh(&txqi->queue.lock);
return ret;
}
static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
struct net_device *dev, struct sta_info *sta, struct net_device *dev, struct sta_info *sta,
struct ieee80211_fast_tx *fast_tx, struct ieee80211_fast_tx *fast_tx,
@ -2821,6 +3008,10 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_stats(dev, skb->len + extra_head); ieee80211_tx_stats(dev, skb->len + extra_head);
if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
return true;
/* will not be crypto-handled beyond what we do here, so use false /* will not be crypto-handled beyond what we do here, so use false
* as the may-encrypt argument for the resize to not account for * as the may-encrypt argument for the resize to not account for
* more room than we already have in 'extra_head' * more room than we already have in 'extra_head'

View file

@ -2724,8 +2724,9 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
rate = cfg80211_calculate_bitrate(&ri); rate = cfg80211_calculate_bitrate(&ri);
if (WARN_ONCE(!rate, if (WARN_ONCE(!rate,
"Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n", "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n",
status->flag, status->rate_idx, status->vht_nss)) (unsigned long long)status->flag, status->rate_idx,
status->vht_nss))
return 0; return 0;
/* rewind from end of MPDU */ /* rewind from end of MPDU */

View file

@ -504,18 +504,20 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
!ieee80211_is_robust_mgmt_frame(skb)) !ieee80211_is_robust_mgmt_frame(skb))
return RX_CONTINUE; return RX_CONTINUE;
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
if (status->flag & RX_FLAG_DECRYPTED) { if (status->flag & RX_FLAG_DECRYPTED) {
if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN)) if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
if (status->flag & RX_FLAG_MIC_STRIPPED)
mic_len = 0;
} else { } else {
if (skb_linearize(rx->skb)) if (skb_linearize(rx->skb))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
if (!(status->flag & RX_FLAG_PN_VALIDATED)) { if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
ccmp_hdr2pn(pn, skb->data + hdrlen); ccmp_hdr2pn(pn, skb->data + hdrlen);
@ -720,8 +722,7 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb; struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
u8 pn[IEEE80211_GCMP_PN_LEN]; u8 pn[IEEE80211_GCMP_PN_LEN];
int data_len; int data_len, queue, mic_len = IEEE80211_GCMP_MIC_LEN;
int queue;
hdrlen = ieee80211_hdrlen(hdr->frame_control); hdrlen = ieee80211_hdrlen(hdr->frame_control);
@ -729,19 +730,20 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
!ieee80211_is_robust_mgmt_frame(skb)) !ieee80211_is_robust_mgmt_frame(skb))
return RX_CONTINUE; return RX_CONTINUE;
data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN -
IEEE80211_GCMP_MIC_LEN;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
if (status->flag & RX_FLAG_DECRYPTED) { if (status->flag & RX_FLAG_DECRYPTED) {
if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN)) if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
if (status->flag & RX_FLAG_MIC_STRIPPED)
mic_len = 0;
} else { } else {
if (skb_linearize(rx->skb)) if (skb_linearize(rx->skb))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
if (!(status->flag & RX_FLAG_PN_VALIDATED)) { if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
gcmp_hdr2pn(pn, skb->data + hdrlen); gcmp_hdr2pn(pn, skb->data + hdrlen);
@ -772,7 +774,7 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
} }
/* Remove GCMP header and MIC */ /* Remove GCMP header and MIC */
if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN)) if (pskb_trim(skb, skb->len - mic_len))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen); memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
skb_pull(skb, IEEE80211_GCMP_HDR_LEN); skb_pull(skb, IEEE80211_GCMP_HDR_LEN);

View file

@ -192,7 +192,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
u8 genmask = nft_genmask_cur(read_pnet(&set->pnet)); u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
int err; int err;
err = rhashtable_walk_init(&priv->ht, &hti); err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
iter->err = err; iter->err = err;
if (err) if (err)
return; return;
@ -248,7 +248,7 @@ static void nft_hash_gc(struct work_struct *work)
priv = container_of(work, struct nft_hash, gc_work.work); priv = container_of(work, struct nft_hash, gc_work.work);
set = nft_set_container_of(priv); set = nft_set_container_of(priv);
err = rhashtable_walk_init(&priv->ht, &hti); err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
if (err) if (err)
goto schedule; goto schedule;

View file

@ -2343,7 +2343,8 @@ static int netlink_walk_start(struct nl_seq_iter *iter)
{ {
int err; int err;
err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti); err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti,
GFP_KERNEL);
if (err) { if (err) {
iter->link = MAX_LINKS; iter->link = MAX_LINKS;
return err; return err;

View file

@ -1141,6 +1141,7 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
{ {
struct rfkill *rfkill; struct rfkill *rfkill;
struct rfkill_event ev; struct rfkill_event ev;
int ret;
/* we don't need the 'hard' variable but accept it */ /* we don't need the 'hard' variable but accept it */
if (count < RFKILL_EVENT_SIZE_V1 - 1) if (count < RFKILL_EVENT_SIZE_V1 - 1)
@ -1155,29 +1156,36 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
if (copy_from_user(&ev, buf, count)) if (copy_from_user(&ev, buf, count))
return -EFAULT; return -EFAULT;
if (ev.op != RFKILL_OP_CHANGE && ev.op != RFKILL_OP_CHANGE_ALL)
return -EINVAL;
if (ev.type >= NUM_RFKILL_TYPES) if (ev.type >= NUM_RFKILL_TYPES)
return -EINVAL; return -EINVAL;
mutex_lock(&rfkill_global_mutex); mutex_lock(&rfkill_global_mutex);
if (ev.op == RFKILL_OP_CHANGE_ALL) switch (ev.op) {
case RFKILL_OP_CHANGE_ALL:
rfkill_update_global_state(ev.type, ev.soft); rfkill_update_global_state(ev.type, ev.soft);
list_for_each_entry(rfkill, &rfkill_list, node)
list_for_each_entry(rfkill, &rfkill_list, node) { if (rfkill->type == ev.type ||
if (rfkill->idx != ev.idx && ev.op != RFKILL_OP_CHANGE_ALL) ev.type == RFKILL_TYPE_ALL)
continue; rfkill_set_block(rfkill, ev.soft);
ret = 0;
if (rfkill->type != ev.type && ev.type != RFKILL_TYPE_ALL) break;
continue; case RFKILL_OP_CHANGE:
list_for_each_entry(rfkill, &rfkill_list, node)
rfkill_set_block(rfkill, ev.soft); if (rfkill->idx == ev.idx &&
(rfkill->type == ev.type ||
ev.type == RFKILL_TYPE_ALL))
rfkill_set_block(rfkill, ev.soft);
ret = 0;
break;
default:
ret = -EINVAL;
break;
} }
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
return count; return ret ?: count;
} }
static int rfkill_fop_release(struct inode *inode, struct file *file) static int rfkill_fop_release(struct inode *inode, struct file *file)

View file

@ -319,7 +319,8 @@ static int sctp_transport_walk_start(struct seq_file *seq)
struct sctp_ht_iter *iter = seq->private; struct sctp_ht_iter *iter = seq->private;
int err; int err;
err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti); err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti,
GFP_KERNEL);
if (err) if (err)
return err; return err;

View file

@ -626,6 +626,13 @@ int wiphy_register(struct wiphy *wiphy)
!rdev->ops->set_mac_acl))) !rdev->ops->set_mac_acl)))
return -EINVAL; return -EINVAL;
/* assure only valid behaviours are flagged by driver
* hence subtract 2 as bit 0 is invalid.
*/
if (WARN_ON(wiphy->bss_select_support &&
(wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2))))
return -EINVAL;
if (wiphy->addresses) if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);

View file

@ -402,6 +402,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
[NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, [NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
[NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_SUPPORT_P2P_PS] = { .type = NLA_U8 },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
@ -486,6 +488,15 @@ nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
[NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 }, [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
}; };
static const struct nla_policy
nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
[NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG },
[NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 },
[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = {
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
},
};
static int nl80211_prepare_wdev_dump(struct sk_buff *skb, static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
struct netlink_callback *cb, struct netlink_callback *cb,
struct cfg80211_registered_device **rdev, struct cfg80211_registered_device **rdev,
@ -1731,6 +1742,25 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
rdev->wiphy.ext_features)) rdev->wiphy.ext_features))
goto nla_put_failure; goto nla_put_failure;
if (rdev->wiphy.bss_select_support) {
struct nlattr *nested;
u32 bss_select_support = rdev->wiphy.bss_select_support;
nested = nla_nest_start(msg, NL80211_ATTR_BSS_SELECT);
if (!nested)
goto nla_put_failure;
i = 0;
while (bss_select_support) {
if ((bss_select_support & 1) &&
nla_put_flag(msg, i))
goto nla_put_failure;
i++;
bss_select_support >>= 1;
}
nla_nest_end(msg, nested);
}
/* done */ /* done */
state->split_start = 0; state->split_start = 0;
break; break;
@ -3977,6 +4007,10 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
statype != CFG80211_STA_AP_CLIENT_UNASSOC) statype != CFG80211_STA_AP_CLIENT_UNASSOC)
return -EINVAL; return -EINVAL;
if (params->support_p2p_ps != -1 &&
statype != CFG80211_STA_AP_CLIENT_UNASSOC)
return -EINVAL;
if (params->aid && if (params->aid &&
!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
statype != CFG80211_STA_AP_CLIENT_UNASSOC) statype != CFG80211_STA_AP_CLIENT_UNASSOC)
@ -4270,6 +4304,18 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
else else
params.listen_interval = -1; params.listen_interval = -1;
if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
u8 tmp;
tmp = nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
if (tmp >= NUM_NL80211_P2P_PS_STATUS)
return -EINVAL;
params.support_p2p_ps = tmp;
} else {
params.support_p2p_ps = -1;
}
if (!info->attrs[NL80211_ATTR_MAC]) if (!info->attrs[NL80211_ATTR_MAC])
return -EINVAL; return -EINVAL;
@ -4393,6 +4439,23 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.listen_interval = params.listen_interval =
nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
u8 tmp;
tmp = nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
if (tmp >= NUM_NL80211_P2P_PS_STATUS)
return -EINVAL;
params.support_p2p_ps = tmp;
} else {
/*
* if not specified, assume it's supported for P2P GO interface,
* and is NOT supported for AP interface
*/
params.support_p2p_ps =
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO;
}
if (info->attrs[NL80211_ATTR_PEER_AID]) if (info->attrs[NL80211_ATTR_PEER_AID])
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
else else
@ -5758,6 +5821,73 @@ static int validate_scan_freqs(struct nlattr *freqs)
return n_channels; return n_channels;
} }
static bool is_band_valid(struct wiphy *wiphy, enum ieee80211_band b)
{
return b < IEEE80211_NUM_BANDS && wiphy->bands[b];
}
static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy,
struct cfg80211_bss_selection *bss_select)
{
struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1];
struct nlattr *nest;
int err;
bool found = false;
int i;
/* only process one nested attribute */
nest = nla_data(nla);
if (!nla_ok(nest, nla_len(nest)))
return -EINVAL;
err = nla_parse(attr, NL80211_BSS_SELECT_ATTR_MAX, nla_data(nest),
nla_len(nest), nl80211_bss_select_policy);
if (err)
return err;
/* only one attribute may be given */
for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) {
if (attr[i]) {
if (found)
return -EINVAL;
found = true;
}
}
bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID;
if (attr[NL80211_BSS_SELECT_ATTR_RSSI])
bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI;
if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) {
bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF;
bss_select->param.band_pref =
nla_get_u32(attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]);
if (!is_band_valid(wiphy, bss_select->param.band_pref))
return -EINVAL;
}
if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) {
struct nl80211_bss_select_rssi_adjust *adj_param;
adj_param = nla_data(attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]);
bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST;
bss_select->param.adjust.band = adj_param->band;
bss_select->param.adjust.delta = adj_param->delta;
if (!is_band_valid(wiphy, bss_select->param.adjust.band))
return -EINVAL;
}
/* user-space did not provide behaviour attribute */
if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID)
return -EINVAL;
if (!(wiphy->bss_select_support & BIT(bss_select->behaviour)))
return -EINVAL;
return 0;
}
static int nl80211_parse_random_mac(struct nlattr **attrs, static int nl80211_parse_random_mac(struct nlattr **attrs,
u8 *mac_addr, u8 *mac_addr_mask) u8 *mac_addr, u8 *mac_addr_mask)
{ {
@ -5996,6 +6126,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->no_cck = request->no_cck =
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
if (info->attrs[NL80211_ATTR_MAC])
memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]),
ETH_ALEN);
else
eth_broadcast_addr(request->bssid);
request->wdev = wdev; request->wdev = wdev;
request->wiphy = &rdev->wiphy; request->wiphy = &rdev->wiphy;
request->scan_start = jiffies; request->scan_start = jiffies;
@ -7922,6 +8058,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
connect.mfp = NL80211_MFP_NO; connect.mfp = NL80211_MFP_NO;
} }
if (info->attrs[NL80211_ATTR_PREV_BSSID])
connect.prev_bssid =
nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
connect.channel = nl80211_get_valid_chan( connect.channel = nl80211_get_valid_chan(
wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]); wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
@ -7995,8 +8135,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (info->attrs[NL80211_ATTR_BSS_SELECT]) {
/* bss selection makes no sense if bssid is set */
if (connect.bssid) {
kzfree(connkeys);
return -EINVAL;
}
err = parse_bss_select(info->attrs[NL80211_ATTR_BSS_SELECT],
wiphy, &connect.bss_select);
if (err) {
kzfree(connkeys);
return err;
}
}
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); err = cfg80211_connect(rdev, dev, &connect, connkeys,
connect.prev_bssid);
wdev_unlock(dev->ieee80211_ptr); wdev_unlock(dev->ieee80211_ptr);
if (err) if (err)
kzfree(connkeys); kzfree(connkeys);

View file

@ -1293,6 +1293,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,
if (wiphy->bands[i]) if (wiphy->bands[i])
creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
eth_broadcast_addr(creq->bssid);
rdev->scan_req = creq; rdev->scan_req = creq;
err = rdev_scan(rdev, creq); err = rdev_scan(rdev, creq);
if (err) { if (err) {

View file

@ -119,6 +119,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
wdev->conn->params.ssid_len); wdev->conn->params.ssid_len);
request->ssids[0].ssid_len = wdev->conn->params.ssid_len; request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
eth_broadcast_addr(request->bssid);
request->wdev = wdev; request->wdev = wdev;
request->wiphy = &rdev->wiphy; request->wiphy = &rdev->wiphy;
request->scan_start = jiffies; request->scan_start = jiffies;
@ -490,8 +492,18 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
if (!rdev->ops->auth || !rdev->ops->assoc) if (!rdev->ops->auth || !rdev->ops->assoc)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (wdev->current_bss) if (wdev->current_bss) {
return -EALREADY; if (!prev_bssid)
return -EALREADY;
if (prev_bssid &&
!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
return -ENOTCONN;
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
cfg80211_sme_free(wdev);
}
if (WARN_ON(wdev->conn)) if (WARN_ON(wdev->conn))
return -EINPROGRESS; return -EINPROGRESS;

View file

@ -1259,6 +1259,7 @@ TRACE_EVENT(rdev_connect,
__field(bool, privacy) __field(bool, privacy)
__field(u32, wpa_versions) __field(u32, wpa_versions)
__field(u32, flags) __field(u32, flags)
MAC_ENTRY(prev_bssid)
), ),
TP_fast_assign( TP_fast_assign(
WIPHY_ASSIGN; WIPHY_ASSIGN;
@ -1270,13 +1271,14 @@ TRACE_EVENT(rdev_connect,
__entry->privacy = sme->privacy; __entry->privacy = sme->privacy;
__entry->wpa_versions = sme->crypto.wpa_versions; __entry->wpa_versions = sme->crypto.wpa_versions;
__entry->flags = sme->flags; __entry->flags = sme->flags;
MAC_ASSIGN(prev_bssid, sme->prev_bssid);
), ),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, " ", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, "
"flags: %u", "flags: %u, previous bssid: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid,
__entry->auth_type, BOOL_TO_STR(__entry->privacy), __entry->auth_type, BOOL_TO_STR(__entry->privacy),
__entry->wpa_versions, __entry->flags) __entry->wpa_versions, __entry->flags, MAC_PR_ARG(prev_bssid))
); );
TRACE_EVENT(rdev_set_cqm_rssi_config, TRACE_EVENT(rdev_set_cqm_rssi_config,

View file

@ -399,7 +399,10 @@ static int __init wireless_nlevent_init(void)
if (err) if (err)
return err; return err;
return register_netdevice_notifier(&wext_netdev_notifier); err = register_netdevice_notifier(&wext_netdev_notifier);
if (err)
unregister_pernet_subsys(&wext_pernet_ops);
return err;
} }
subsys_initcall(wireless_nlevent_init); subsys_initcall(wireless_nlevent_init);