Lots of changes:

* aggregation handling improvements for some drivers
  * hidden AP discovery on 6 GHz and other HE 6 GHz
    improvements
  * minstrel improvements for no-ack frames
  * deferred rate control for TXQs to improve reaction
    times
  * virtual time-based airtime scheduler
  * along with various little cleanups/fixups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEH1e1rEeCd0AIMq6MB8qZga/fl8QFAmDWUHgACgkQB8qZga/f
 l8RxXA/+KjkrFUGDHlMTIzS/i03EcB+idLYy+XBWYuO3PN4p/uCCXLzdGfiRKTxp
 iCAjYp0ZP01FlN4sOIN+qqrUMoN0e8xqBEY1DFbJELBB1knV4VR0FeBPPcBMUEgE
 xAjdZnIsfKO1yU2mrdQVDnkvXr4jHDALePvGwCQe+4KUbwCmVjo6nb534v17Ie3C
 MDNnGq395fCvo9NW+Yvzw6s9ZLdhr28bGhSBgBrqmPBx/9vm3b7BA8qO+X6BzAny
 87S2x+z40wabBujCGMvw9J5H6OJ0ZLfbT0TumNznqQbCbmwRdSUC+W/cjgRq/aQn
 CA3E9T7tofIr1mk9EgcjE8ax91TB/TTX5Nh9Huki25B7rNRBM57VnuEuzmXP5qBS
 xgsr60agQHp2ZAGRPBC9msPcPCbvRfdl9ckTAF8/4vI7uEXSyYViX92M0F1FEy60
 Mw13B8WeRA4bH22xHpqdXsyl0x2OQHXHlLHfpzmhcUy0i2cQOQMsWbl3g6CJMTq+
 /InNSaG+cqrCVse734KJLumRhOq4+MkedYJlqL7uHZJrXlLgQ9Z8ewANjJ/BjTsQ
 d8I16XkKPyv8UiJEwHZ6NaHvU48LR8T/r4ym9c/rz0DTWHZyp87QMYa55iwuk4Zo
 cW7G3f/N7dMk9fGRxgfIrnNwNUCMsGcBSJeJyr5qT7u8+RW0bRo=
 =nFvj
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-net-next-2021-06-25' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes berg says:

====================
Lots of changes:
 * aggregation handling improvements for some drivers
 * hidden AP discovery on 6 GHz and other HE 6 GHz
   improvements
 * minstrel improvements for no-ack frames
 * deferred rate control for TXQs to improve reaction
   times
 * virtual time-based airtime scheduler
 * along with various little cleanups/fixups
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-06-28 13:06:12 -07:00
commit 007b312c6f
47 changed files with 1500 additions and 794 deletions

View file

@ -2654,7 +2654,7 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 duration)
struct ieee80211_prep_tx_info *info)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);

View file

@ -3306,14 +3306,14 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 req_duration)
struct ieee80211_prep_tx_info *info)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS;
if (req_duration > duration)
duration = req_duration;
if (info->duration > duration)
duration = info->duration;
mutex_lock(&mvm->mutex);
/* Try really hard to protect the session and hear a beacon

View file

@ -1032,6 +1032,9 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA))
return -1;
if (unlikely(ieee80211_is_any_nullfunc(fc)) && sta->he_cap.has_he)
return -1;
if (unlikely(ieee80211_is_probe_resp(fc)))
iwl_mvm_probe_resp_set_noa(mvm, skb);

View file

@ -626,6 +626,7 @@ struct mac80211_hwsim_data {
u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
struct mac_address addresses[2];
struct ieee80211_chanctx_conf *chanctx;
int channels, idx;
bool use_chanctx;
bool destroy_on_close;
@ -1257,7 +1258,8 @@ static inline u16 trans_tx_rate_flags_ieee2hwsim(struct ieee80211_tx_rate *rate)
static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
struct sk_buff *my_skb,
int dst_portid)
int dst_portid,
struct ieee80211_channel *channel)
{
struct sk_buff *skb;
struct mac80211_hwsim_data *data = hw->priv;
@ -1312,7 +1314,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags))
goto nla_put_failure;
if (nla_put_u32(skb, HWSIM_ATTR_FREQ, data->channel->center_freq))
if (nla_put_u32(skb, HWSIM_ATTR_FREQ, channel->center_freq))
goto nla_put_failure;
/* We get the tx control (rate and retries) info*/
@ -1659,7 +1661,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
_portid = READ_ONCE(data->wmediumd);
if (_portid || hwsim_virtio_enabled)
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, channel);
/* NO wmediumd detected, perfect medium simulation */
data->tx_pkts++;
@ -1775,8 +1777,10 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
mac80211_hwsim_monitor_rx(hw, skb, chan);
if (_pid || hwsim_virtio_enabled)
return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
return mac80211_hwsim_tx_frame_nl(hw, skb, _pid, chan);
data->tx_pkts++;
data->tx_bytes += skb->len;
mac80211_hwsim_tx_frame_no_nl(hw, skb, chan);
dev_kfree_skb(skb);
}
@ -2514,6 +2518,11 @@ static int mac80211_hwsim_croc(struct ieee80211_hw *hw,
static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
mutex_lock(&hwsim->mutex);
hwsim->chanctx = ctx;
mutex_unlock(&hwsim->mutex);
hwsim_set_chanctx_magic(ctx);
wiphy_dbg(hw->wiphy,
"add channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n",
@ -2525,6 +2534,11 @@ static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw,
static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
mutex_lock(&hwsim->mutex);
hwsim->chanctx = NULL;
mutex_unlock(&hwsim->mutex);
wiphy_dbg(hw->wiphy,
"remove channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n",
ctx->def.chan->center_freq, ctx->def.width,
@ -2537,6 +2551,11 @@ static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx,
u32 changed)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
mutex_lock(&hwsim->mutex);
hwsim->chanctx = ctx;
mutex_unlock(&hwsim->mutex);
hwsim_check_chanctx_magic(ctx);
wiphy_dbg(hw->wiphy,
"change channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n",
@ -3129,6 +3148,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->wiphy->max_remain_on_channel_duration = 1000;
data->if_combination.radar_detect_widths = 0;
data->if_combination.num_different_channels = data->channels;
data->chanctx = NULL;
} else {
data->if_combination.num_different_channels = 1;
data->if_combination.radar_detect_widths =
@ -3638,6 +3658,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
int frame_data_len;
void *frame_data;
struct sk_buff *skb = NULL;
struct ieee80211_channel *channel = NULL;
if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
!info->attrs[HWSIM_ATTR_FRAME] ||
@ -3664,6 +3685,17 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
if (!data2)
goto out;
if (data2->use_chanctx) {
if (data2->tmp_chan)
channel = data2->tmp_chan;
else if (data2->chanctx)
channel = data2->chanctx->def.chan;
} else {
channel = data2->channel;
}
if (!channel)
goto out;
if (!hwsim_virtio_enabled) {
if (hwsim_net_get_netgroup(genl_info_net(info)) !=
data2->netgroup)
@ -3675,7 +3707,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
/* check if radio is configured properly */
if (data2->idle || !data2->started)
if ((data2->idle && !data2->tmp_chan) || !data2->started)
goto out;
/* A frame is received from user space */
@ -3688,18 +3720,16 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
mutex_lock(&data2->mutex);
rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]);
if (rx_status.freq != data2->channel->center_freq &&
(!data2->tmp_chan ||
rx_status.freq != data2->tmp_chan->center_freq)) {
if (rx_status.freq != channel->center_freq) {
mutex_unlock(&data2->mutex);
goto out;
}
mutex_unlock(&data2->mutex);
} else {
rx_status.freq = data2->channel->center_freq;
rx_status.freq = channel->center_freq;
}
rx_status.band = data2->channel->band;
rx_status.band = channel->band;
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
@ -3796,11 +3826,6 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
return -EINVAL;
}
if (param.channels > CFG80211_MAX_NUM_DIFFERENT_CHANNELS) {
GENL_SET_ERR_MSG(info, "too many channels specified");
return -EINVAL;
}
if (info->attrs[HWSIM_ATTR_NO_VIF])
param.no_vif = true;

View file

@ -638,7 +638,7 @@ static void rtw_ops_sw_scan_complete(struct ieee80211_hw *hw,
static void rtw_ops_mgd_prepare_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 duration)
struct ieee80211_prep_tx_info *info)
{
struct rtw_dev *rtwdev = hw->priv;

View file

@ -9,7 +9,7 @@
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
* Copyright (c) 2018 - 2020 Intel Corporation
* Copyright (c) 2018 - 2021 Intel Corporation
*/
#ifndef LINUX_IEEE80211_H
@ -2179,6 +2179,8 @@ int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED 0xc0
#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK 0xc0
#define IEEE80211_HE_PHY_CAP10_HE_MU_M1RU_MAX_LTF 0x01
/* 802.11ax HE TX/RX MCS NSS Support */
#define IEEE80211_TX_RX_MCS_NSS_SUPP_HIGHEST_MCS_POS (3)
#define IEEE80211_TX_RX_MCS_NSS_SUPP_TX_BITMAP_POS (6)
@ -2933,6 +2935,7 @@ enum ieee80211_category {
WLAN_CATEGORY_BACK = 3,
WLAN_CATEGORY_PUBLIC = 4,
WLAN_CATEGORY_RADIO_MEASUREMENT = 5,
WLAN_CATEGORY_FAST_BBS_TRANSITION = 6,
WLAN_CATEGORY_HT = 7,
WLAN_CATEGORY_SA_QUERY = 8,
WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9,
@ -3110,6 +3113,11 @@ enum ieee80211_tdls_actioncode {
*/
#define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT BIT(6)
/* Timing Measurement protocol for time sync is set in the 7th bit of 3rd byte
* of the @WLAN_EID_EXT_CAPABILITY information element
*/
#define WLAN_EXT_CAPA3_TIMING_MEASUREMENT_SUPPORT BIT(7)
/* TDLS capabilities in the 4th byte of @WLAN_EID_EXT_CAPABILITY */
#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)

View file

@ -7,7 +7,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*/
#include <linux/ethtool.h>
@ -22,6 +22,7 @@
#include <linux/if_ether.h>
#include <linux/ieee80211.h>
#include <linux/net.h>
#include <linux/rfkill.h>
#include <net/regulatory.h>
/**
@ -370,11 +371,18 @@ struct ieee80211_sta_he_cap {
* @he_cap: holds the HE capabilities
* @he_6ghz_capa: HE 6 GHz capabilities, must be filled in for a
* 6 GHz band channel (and 0 may be valid value).
* @vendor_elems: vendor element(s) to advertise
* @vendor_elems.data: vendor element(s) data
* @vendor_elems.len: vendor element(s) length
*/
struct ieee80211_sband_iftype_data {
u16 types_mask;
struct ieee80211_sta_he_cap he_cap;
struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct {
const u8 *data;
unsigned int len;
} vendor_elems;
};
/**
@ -533,18 +541,6 @@ ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *sband,
return NULL;
}
/**
* ieee80211_get_he_sta_cap - return HE capabilities for an sband's STA
* @sband: the sband to search for the STA on
*
* Return: pointer to the struct ieee80211_sta_he_cap, or NULL is none found
*/
static inline const struct ieee80211_sta_he_cap *
ieee80211_get_he_sta_cap(const struct ieee80211_supported_band *sband)
{
return ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_STATION);
}
/**
* ieee80211_get_he_6ghz_capa - return HE 6 GHz capabilities
* @sband: the sband to search for the STA on
@ -905,6 +901,17 @@ ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
return chandef->chan->max_power;
}
/**
* cfg80211_any_usable_channels - check for usable channels
* @wiphy: the wiphy to check for
* @band_mask: which bands to check on
* @prohibited_flags: which channels to not consider usable,
* %IEEE80211_CHAN_DISABLED is always taken into account
*/
bool cfg80211_any_usable_channels(struct wiphy *wiphy,
unsigned long band_mask,
u32 prohibited_flags);
/**
* enum survey_info_flags - survey information flags
*
@ -1245,8 +1252,6 @@ struct cfg80211_csa_settings {
u8 count;
};
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
/**
* struct iface_combination_params - input parameters for interface combinations
*
@ -3522,7 +3527,10 @@ struct cfg80211_pmsr_result {
* If neither @trigger_based nor @non_trigger_based is set,
* EDCA based ranging will be used.
* @lmr_feedback: negotiate for I2R LMR feedback. Only valid if either
* @trigger_based or @non_trigger_based is set.
* @trigger_based or @non_trigger_based is set.
* @bss_color: the bss color of the responder. Optional. Set to zero to
* indicate the driver should set the BSS color. Only valid if
* @non_trigger_based or @trigger_based is set.
*
* See also nl80211 for the respective attribute documentation.
*/
@ -3540,6 +3548,7 @@ struct cfg80211_pmsr_ftm_request_peer {
u8 burst_duration;
u8 ftms_per_burst;
u8 ftmr_retries;
u8 bss_color;
};
/**
@ -4945,6 +4954,7 @@ struct wiphy_iftype_akm_suites {
* configuration through the %NL80211_TID_CONFIG_ATTR_RETRY_SHORT and
* %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
* @sar_capa: SAR control capabilities
* @rfkill: a pointer to the rfkill structure
*/
struct wiphy {
struct mutex mtx;
@ -5087,6 +5097,8 @@ struct wiphy {
const struct cfg80211_sar_capa *sar_capa;
struct rfkill *rfkill;
char priv[] __aligned(NETDEV_ALIGN);
};
@ -6661,7 +6673,10 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy);
* wiphy_rfkill_stop_polling - stop polling rfkill
* @wiphy: the wiphy
*/
void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
static inline void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
{
rfkill_pause_polling(wiphy->rfkill);
}
/**
* DOC: Vendor commands

View file

@ -7,7 +7,7 @@
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
* Copyright (C) 2018 - 2020 Intel Corporation
* Copyright (C) 2018 - 2021 Intel Corporation
*/
#ifndef MAC80211_H
@ -526,6 +526,7 @@ struct ieee80211_fils_discovery {
* @twt_responder: does this BSS support TWT requester (relevant for managed
* mode only, set if the AP advertises TWT responder role)
* @twt_protected: does this BSS support protected TWT frames
* @twt_broadcast: does this BSS support broadcast TWT
* @assoc: association status
* @ibss_joined: indicates whether this station is part of an IBSS
* or not
@ -642,6 +643,7 @@ struct ieee80211_bss_conf {
bool twt_requester;
bool twt_responder;
bool twt_protected;
bool twt_broadcast;
/* association related data */
bool assoc, ibss_joined;
bool ibss_creator;
@ -3344,6 +3346,21 @@ enum ieee80211_reconfig_type {
IEEE80211_RECONFIG_TYPE_SUSPEND,
};
/**
* struct ieee80211_prep_tx_info - prepare TX information
* @duration: if non-zero, hint about the required duration,
* only used with the mgd_prepare_tx() method.
* @subtype: frame subtype (auth, (re)assoc, deauth, disassoc)
* @success: whether the frame exchange was successful, only
* used with the mgd_complete_tx() method, and then only
* valid for auth and (re)assoc.
*/
struct ieee80211_prep_tx_info {
u16 duration;
u16 subtype;
u8 success:1;
};
/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
@ -3756,9 +3773,13 @@ enum ieee80211_reconfig_type {
* frame in case that no beacon was heard from the AP/P2P GO.
* The callback will be called before each transmission and upon return
* mac80211 will transmit the frame right away.
* If duration is greater than zero, mac80211 hints to the driver the
* duration for which the operation is requested.
* Additional information is passed in the &struct ieee80211_prep_tx_info
* data. If duration there is greater than zero, mac80211 hints to the
* driver the duration for which the operation is requested.
* The callback is optional and can (should!) sleep.
* @mgd_complete_tx: Notify the driver that the response frame for a previously
* transmitted frame announced with @mgd_prepare_tx was received, the data
* is filled similarly to @mgd_prepare_tx though the duration is not used.
*
* @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
* a TDLS discovery-request, we expect a reply to arrive on the AP's
@ -4109,7 +4130,10 @@ struct ieee80211_ops {
void (*mgd_prepare_tx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 duration);
struct ieee80211_prep_tx_info *info);
void (*mgd_complete_tx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_prep_tx_info *info);
void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
@ -6184,6 +6208,11 @@ enum rate_control_capabilities {
* otherwise the NSS difference doesn't bother us.
*/
RATE_CTRL_CAPA_VHT_EXT_NSS_BW = BIT(0),
/**
* @RATE_CTRL_CAPA_AMPDU_TRIGGER:
* mac80211 should start A-MPDU sessions on tx
*/
RATE_CTRL_CAPA_AMPDU_TRIGGER = BIT(1),
};
struct rate_control_ops {
@ -6576,9 +6605,6 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
{
}
void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq, bool force);
/**
* ieee80211_schedule_txq - schedule a TXQ for transmission
*
@ -6591,11 +6617,7 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
* The driver may call this function if it has buffered packets for
* this TXQ internally.
*/
static inline void
ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{
__ieee80211_schedule_txq(hw, txq, true);
}
void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
/**
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
@ -6607,12 +6629,8 @@ ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
* The driver may set force=true if it has buffered packets for this TXQ
* internally.
*/
static inline void
ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool force)
{
__ieee80211_schedule_txq(hw, txq, force);
}
void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
bool force);
/**
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
@ -6752,4 +6770,22 @@ struct sk_buff *ieee80211_get_fils_discovery_tmpl(struct ieee80211_hw *hw,
struct sk_buff *
ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
/**
* ieee80211_is_tx_data - check if frame is a data frame
*
* The function is used to check if a frame is a data frame. Frames with
* hardware encapsulation enabled are data frames.
*
* @skb: the frame to be transmitted.
*/
static inline bool ieee80211_is_tx_data(struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *) skb->data;
return info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP ||
ieee80211_is_data(hdr->frame_control);
}
#endif /* MAC80211_H */

View file

@ -11,7 +11,7 @@
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
* Copyright 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -3654,6 +3654,8 @@ enum nl80211_mpath_info {
* defined
* @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
* given for all 6 GHz band channels
* @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
* advertised on this band/for this iftype (binary)
* @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
*/
enum nl80211_band_iftype_attr {
@ -3665,6 +3667,7 @@ enum nl80211_band_iftype_attr {
NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
/* keep last */
__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@ -6912,6 +6915,9 @@ enum nl80211_peer_measurement_ftm_capa {
* @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
* valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
* %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
* @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
* responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
* or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
*
* @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
* @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
@ -6931,6 +6937,7 @@ enum nl80211_peer_measurement_ftm_req {
NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
/* keep last */
NUM_NL80211_PMSR_FTM_REQ_ATTR,

View file

@ -1442,6 +1442,38 @@ static void sta_apply_mesh_params(struct ieee80211_local *local,
#endif
}
static void sta_apply_airtime_params(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
{
u8 ac;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
struct airtime_sched_info *air_sched = &local->airtime[ac];
struct airtime_info *air_info = &sta->airtime[ac];
struct txq_info *txqi;
u8 tid;
spin_lock_bh(&air_sched->lock);
for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
if (air_info->weight == params->airtime_weight ||
!sta->sta.txq[tid] ||
ac != ieee80211_ac_from_tid(tid))
continue;
airtime_weight_set(air_info, params->airtime_weight);
txqi = to_txq_info(sta->sta.txq[tid]);
if (RB_EMPTY_NODE(&txqi->schedule_order))
continue;
ieee80211_update_airtime_weight(local, air_sched,
0, true);
}
spin_unlock_bh(&air_sched->lock);
}
}
static int sta_apply_parameters(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
@ -1629,7 +1661,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta_apply_mesh_params(local, sta, params);
if (params->airtime_weight)
sta->airtime_weight = params->airtime_weight;
sta_apply_airtime_params(local, sta, params);
/* set the STA state after all sta info from usermode has been set */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
@ -1693,15 +1726,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
test_sta_flag(sta, WLAN_STA_ASSOC))
rate_control_rate_init(sta);
err = sta_info_insert_rcu(sta);
if (err) {
rcu_read_unlock();
return err;
}
rcu_read_unlock();
return 0;
return sta_info_insert(sta);
}
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* mac80211 - channel management
* Copyright 2020 - 2021 Intel Corporation
*/
#include <linux/nl80211.h>
@ -308,8 +309,8 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
* the max of min required widths of all the interfaces bound to this
* channel context.
*/
void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
enum nl80211_chan_width max_bw;
struct cfg80211_chan_def min_def;
@ -326,7 +327,7 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
ctx->conf.def.width == NL80211_CHAN_WIDTH_16 ||
ctx->conf.radar_enabled) {
ctx->conf.min_def = ctx->conf.def;
return;
return 0;
}
max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
@ -337,17 +338,21 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
ieee80211_chandef_downgrade(&min_def);
if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
return;
return 0;
ctx->conf.min_def = min_def;
if (!ctx->driver_present)
return;
return 0;
drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
return IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
}
/* calling this function is assuming that station vif is updated to
* lates changes by calling ieee80211_vif_update_chandef
*/
static void ieee80211_chan_bw_change(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
struct ieee80211_chanctx *ctx,
bool narrowed)
{
struct sta_info *sta;
struct ieee80211_supported_band *sband =
@ -366,9 +371,16 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
continue;
new_sta_bw = ieee80211_sta_cur_vht_bw(sta);
/* nothing change */
if (new_sta_bw == sta->sta.bandwidth)
continue;
/* vif changed to narrow BW and narrow BW for station wasn't
* requested or vise versa */
if ((new_sta_bw < sta->sta.bandwidth) == !narrowed)
continue;
sta->sta.bandwidth = new_sta_bw;
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_BW_CHANGED);
@ -376,21 +388,34 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
rcu_read_unlock();
}
/*
* recalc the min required chan width of the channel context, which is
* the max of min required widths of all the interfaces bound to this
* channel context.
*/
void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx);
if (!changed)
return;
/* check is BW narrowed */
ieee80211_chan_bw_change(local, ctx, true);
drv_change_chanctx(local, ctx, changed);
/* check is BW wider */
ieee80211_chan_bw_change(local, ctx, false);
}
static void ieee80211_change_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
struct ieee80211_chanctx *old_ctx,
const struct cfg80211_chan_def *chandef)
{
enum nl80211_chan_width width;
if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) {
ieee80211_recalc_chanctx_min_def(local, ctx);
return;
}
WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
width = ctx->conf.def.width;
ctx->conf.def = *chandef;
u32 changed;
/* expected to handle only 20/40/80/160 channel widths */
switch (chandef->width) {
@ -405,19 +430,33 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
WARN_ON(1);
}
if (chandef->width < width)
ieee80211_chan_bw_change(local, ctx);
/* Check maybe BW narrowed - we do this _before_ calling recalc_chanctx_min_def
* due to maybe not returning from it, e.g in case new context was added
* first time with all parameters up to date.
*/
ieee80211_chan_bw_change(local, old_ctx, true);
drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
ieee80211_recalc_chanctx_min_def(local, ctx);
if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) {
ieee80211_recalc_chanctx_min_def(local, ctx);
return;
}
WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
ctx->conf.def = *chandef;
/* check if min chanctx also changed */
changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
_ieee80211_recalc_chanctx_min_def(local, ctx);
drv_change_chanctx(local, ctx, changed);
if (!local->use_chanctx) {
local->_oper_chandef = *chandef;
ieee80211_hw_config(local, 0);
}
if (chandef->width > width)
ieee80211_chan_bw_change(local, ctx);
/* check is BW wider */
ieee80211_chan_bw_change(local, old_ctx, false);
}
static struct ieee80211_chanctx *
@ -450,7 +489,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
if (!compat)
continue;
ieee80211_change_chanctx(local, ctx, compat);
ieee80211_change_chanctx(local, ctx, ctx, compat);
return ctx;
}
@ -679,7 +718,7 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
if (!compat)
return;
ieee80211_change_chanctx(local, ctx, compat);
ieee80211_change_chanctx(local, ctx, ctx, compat);
}
static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
@ -1107,13 +1146,12 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
if (WARN_ON(!chandef))
return -EINVAL;
if (old_ctx->conf.def.width > new_ctx->conf.def.width)
ieee80211_chan_bw_change(local, new_ctx);
if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
changed = BSS_CHANGED_BANDWIDTH;
ieee80211_change_chanctx(local, new_ctx, chandef);
ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
if (old_ctx->conf.def.width < new_ctx->conf.def.width)
ieee80211_chan_bw_change(local, new_ctx);
ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef);
vif_chsw[0].vif = &sdata->vif;
vif_chsw[0].old_ctx = &old_ctx->conf;
@ -1142,14 +1180,9 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
ieee80211_free_chanctx(local, old_ctx);
if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
changed = BSS_CHANGED_BANDWIDTH;
ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
ieee80211_recalc_chanctx_min_def(local, new_ctx);
ieee80211_recalc_smps_chanctx(local, new_ctx);
ieee80211_recalc_radar_chanctx(local, new_ctx);
ieee80211_recalc_chanctx_min_def(local, new_ctx);
if (changed)
ieee80211_bss_info_change_notify(sdata, changed);
@ -1188,7 +1221,7 @@ ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
if (WARN_ON(!chandef))
return -EINVAL;
ieee80211_change_chanctx(local, new_ctx, chandef);
ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef);
list_del(&sdata->reserved_chanctx_list);
sdata->reserved_chanctx = NULL;
@ -1505,7 +1538,6 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);
ieee80211_recalc_chanctx_min_def(local, ctx);
ieee80211_chan_bw_change(local, ctx);
list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
reserved_chanctx_list) {

View file

@ -216,14 +216,14 @@ static ssize_t aql_txq_limit_read(struct file *file,
"VI %u %u\n"
"BE %u %u\n"
"BK %u %u\n",
local->aql_txq_limit_low[IEEE80211_AC_VO],
local->aql_txq_limit_high[IEEE80211_AC_VO],
local->aql_txq_limit_low[IEEE80211_AC_VI],
local->aql_txq_limit_high[IEEE80211_AC_VI],
local->aql_txq_limit_low[IEEE80211_AC_BE],
local->aql_txq_limit_high[IEEE80211_AC_BE],
local->aql_txq_limit_low[IEEE80211_AC_BK],
local->aql_txq_limit_high[IEEE80211_AC_BK]);
local->airtime[IEEE80211_AC_VO].aql_txq_limit_low,
local->airtime[IEEE80211_AC_VO].aql_txq_limit_high,
local->airtime[IEEE80211_AC_VI].aql_txq_limit_low,
local->airtime[IEEE80211_AC_VI].aql_txq_limit_high,
local->airtime[IEEE80211_AC_BE].aql_txq_limit_low,
local->airtime[IEEE80211_AC_BE].aql_txq_limit_high,
local->airtime[IEEE80211_AC_BK].aql_txq_limit_low,
local->airtime[IEEE80211_AC_BK].aql_txq_limit_high);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
@ -255,11 +255,11 @@ static ssize_t aql_txq_limit_write(struct file *file,
if (ac >= IEEE80211_NUM_ACS)
return -EINVAL;
q_limit_low_old = local->aql_txq_limit_low[ac];
q_limit_high_old = local->aql_txq_limit_high[ac];
q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
q_limit_high_old = local->airtime[ac].aql_txq_limit_high;
local->aql_txq_limit_low[ac] = q_limit_low;
local->aql_txq_limit_high[ac] = q_limit_high;
local->airtime[ac].aql_txq_limit_low = q_limit_low;
local->airtime[ac].aql_txq_limit_high = q_limit_high;
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
@ -382,6 +382,46 @@ static const struct file_operations force_tx_status_ops = {
.llseek = default_llseek,
};
static ssize_t airtime_read(struct file *file,
char __user *user_buf,
size_t count,
loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[200];
u64 v_t[IEEE80211_NUM_ACS];
u64 wt[IEEE80211_NUM_ACS];
int len = 0, ac;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->airtime[ac].lock);
v_t[ac] = local->airtime[ac].v_t;
wt[ac] = local->airtime[ac].weight_sum;
spin_unlock_bh(&local->airtime[ac].lock);
}
len = scnprintf(buf, sizeof(buf),
"\tVO VI BE BK\n"
"Virt-t\t%-10llu %-10llu %-10llu %-10llu\n"
"Weight\t%-10llu %-10llu %-10llu %-10llu\n",
v_t[0],
v_t[1],
v_t[2],
v_t[3],
wt[0],
wt[1],
wt[2],
wt[3]);
return simple_read_from_buffer(user_buf, count, ppos,
buf, len);
}
static const struct file_operations airtime_ops = {
.read = airtime_read,
.open = simple_open,
.llseek = default_llseek,
};
#ifdef CONFIG_PM
static ssize_t reset_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
@ -632,7 +672,11 @@ void debugfs_hw_add(struct ieee80211_local *local)
if (local->ops->wake_tx_queue)
DEBUGFS_ADD_MODE(aqm, 0600);
DEBUGFS_ADD_MODE(airtime_flags, 0600);
if (wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
DEBUGFS_ADD_MODE(airtime, 0600);
DEBUGFS_ADD_MODE(airtime_flags, 0600);
}
DEBUGFS_ADD(aql_txq_limit);
debugfs_create_u32("aql_threshold", 0600,

View file

@ -57,7 +57,6 @@ static ssize_t ieee80211_if_write(
return -EFAULT;
buf[count] = '\0';
ret = -ENODEV;
rtnl_lock();
ret = (*write)(sdata, buf, count);
rtnl_unlock();
@ -513,6 +512,34 @@ static ssize_t ieee80211_if_fmt_aqm(
}
IEEE80211_IF_FILE_R(aqm);
static ssize_t ieee80211_if_fmt_airtime(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_txq *txq = sdata->vif.txq;
struct airtime_info *air_info;
int len;
if (!txq)
return 0;
spin_lock_bh(&local->airtime[txq->ac].lock);
air_info = to_airtime_info(txq);
len = scnprintf(buf,
buflen,
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
"Virt-T: %lld us\n",
air_info->rx_airtime,
air_info->tx_airtime,
air_info->weight,
air_info->v_t);
spin_unlock_bh(&local->airtime[txq->ac].lock);
return len;
}
IEEE80211_IF_FILE_R(airtime);
IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
/* IBSS attributes */
@ -658,8 +685,10 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
if (sdata->local->ops->wake_tx_queue &&
sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
sdata->vif.type != NL80211_IFTYPE_NAN)
sdata->vif.type != NL80211_IFTYPE_NAN) {
DEBUGFS_ADD(aqm);
DEBUGFS_ADD(airtime);
}
}
static void add_sta_files(struct ieee80211_sub_if_data *sdata)

View file

@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
size_t bufsz = 400;
char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
u64 rx_airtime = 0, tx_airtime = 0;
s64 deficit[IEEE80211_NUM_ACS];
u64 v_t[IEEE80211_NUM_ACS];
ssize_t rv;
int ac;
@ -210,18 +210,18 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
return -ENOMEM;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->active_txq_lock[ac]);
spin_lock_bh(&local->airtime[ac].lock);
rx_airtime += sta->airtime[ac].rx_airtime;
tx_airtime += sta->airtime[ac].tx_airtime;
deficit[ac] = sta->airtime[ac].deficit;
spin_unlock_bh(&local->active_txq_lock[ac]);
v_t[ac] = sta->airtime[ac].v_t;
spin_unlock_bh(&local->airtime[ac].lock);
}
p += scnprintf(p, bufsz + buf - p,
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
"Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
rx_airtime, tx_airtime, sta->airtime_weight,
deficit[0], deficit[1], deficit[2], deficit[3]);
"Virt-T: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
rx_airtime, tx_airtime, sta->airtime[0].weight,
v_t[0], v_t[1], v_t[2], v_t[3]);
rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
kfree(buf);
@ -236,11 +236,11 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
int ac;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->active_txq_lock[ac]);
spin_lock_bh(&local->airtime[ac].lock);
sta->airtime[ac].rx_airtime = 0;
sta->airtime[ac].tx_airtime = 0;
sta->airtime[ac].deficit = sta->airtime_weight;
spin_unlock_bh(&local->active_txq_lock[ac]);
sta->airtime[ac].v_t = 0;
spin_unlock_bh(&local->airtime[ac].lock);
}
return count;
@ -263,10 +263,10 @@ static ssize_t sta_aql_read(struct file *file, char __user *userbuf,
return -ENOMEM;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
spin_lock_bh(&local->active_txq_lock[ac]);
spin_lock_bh(&local->airtime[ac].lock);
q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
spin_unlock_bh(&local->active_txq_lock[ac]);
spin_unlock_bh(&local->airtime[ac].lock);
q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
}

View file

@ -2,7 +2,7 @@
/*
* Portions of this file
* Copyright(c) 2016 Intel Deutschland GmbH
* Copyright (C) 2018 - 2019 Intel Corporation
* Copyright (C) 2018 - 2019, 2021 Intel Corporation
*/
#ifndef __MAC80211_DRIVER_OPS
@ -821,7 +821,7 @@ drv_allow_buffered_frames(struct ieee80211_local *local,
static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 duration)
struct ieee80211_prep_tx_info *info)
{
might_sleep();
@ -829,9 +829,27 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
return;
WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
trace_drv_mgd_prepare_tx(local, sdata, duration);
trace_drv_mgd_prepare_tx(local, sdata, info->duration,
info->subtype, info->success);
if (local->ops->mgd_prepare_tx)
local->ops->mgd_prepare_tx(&local->hw, &sdata->vif, duration);
local->ops->mgd_prepare_tx(&local->hw, &sdata->vif, info);
trace_drv_return_void(local);
}
static inline void drv_mgd_complete_tx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_prep_tx_info *info)
{
might_sleep();
if (!check_sdata_in_driver(sdata))
return;
WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
trace_drv_mgd_complete_tx(local, sdata, info->duration,
info->subtype, info->success);
if (local->ops->mgd_complete_tx)
local->ops->mgd_complete_tx(&local->hw, &sdata->vif, info);
trace_drv_return_void(local);
}

View file

@ -111,7 +111,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap;
struct ieee80211_sta_he_cap own_he_cap = sband->iftype_data->he_cap;
struct ieee80211_sta_he_cap own_he_cap;
struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
u8 he_ppe_size;
u8 mcs_nss_size;
@ -120,9 +120,13 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
memset(he_cap, 0, sizeof(*he_cap));
if (!he_cap_ie || !ieee80211_get_he_sta_cap(sband))
if (!he_cap_ie ||
!ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif)))
return;
own_he_cap = sband->iftype_data->he_cap;
/* Make sure size is OK */
mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem);
he_ppe_size =

View file

@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
* Copyright 2017 Intel Deutschland GmbH
* Copyright(c) 2020 Intel Corporation
* Copyright(c) 2020-2021 Intel Corporation
*/
#include <linux/ieee80211.h>
@ -555,17 +555,15 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
vif->type != NL80211_IFTYPE_AP))
if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
return;
if (vif->type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.driver_smps_mode == smps_mode)
return;
sdata->u.mgd.driver_smps_mode = smps_mode;
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.request_smps_work);
}
if (sdata->u.mgd.driver_smps_mode == smps_mode)
return;
sdata->u.mgd.driver_smps_mode = smps_mode;
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.request_smps_work);
}
/* this might change ... don't want non-open drivers using it */
EXPORT_SYMBOL_GPL(ieee80211_request_smps);

View file

@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*/
#ifndef IEEE80211_I_H
@ -831,17 +831,16 @@ enum txq_info_flags {
* @def_flow: used as a fallback flow when a packet destined to @tin hashes to
* a fq_flow which is already owned by a different tin
* @def_cvars: codel vars for @def_flow
* @frags: used to keep fragments created after dequeue
* @schedule_order: used with ieee80211_local->active_txqs
* @schedule_round: counter to prevent infinite loops on TXQ scheduling
* @frags: used to keep fragments created after dequeue
*/
struct txq_info {
struct fq_tin tin;
struct codel_vars def_cvars;
struct codel_stats cstats;
struct rb_node schedule_order;
struct sk_buff_head frags;
struct list_head schedule_order;
u16 schedule_round;
unsigned long flags;
/* keep last! */
@ -918,6 +917,8 @@ struct ieee80211_sub_if_data {
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
struct mac80211_qos_map __rcu *qos_map;
struct airtime_info airtime[IEEE80211_NUM_ACS];
struct work_struct csa_finalize_work;
bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
struct cfg80211_chan_def csa_chandef;
@ -1130,6 +1131,44 @@ enum mac80211_scan_state {
SCAN_ABORT,
};
/**
* struct airtime_sched_info - state used for airtime scheduling and AQL
*
* @lock: spinlock that protects all the fields in this struct
* @active_txqs: rbtree of currently backlogged queues, sorted by virtual time
* @schedule_pos: the current position maintained while a driver walks the tree
* with ieee80211_next_txq()
* @active_list: list of struct airtime_info structs that were active within
* the last AIRTIME_ACTIVE_DURATION (100 ms), used to compute
* weight_sum
* @last_weight_update: used for rate limiting walking active_list
* @last_schedule_time: tracks the last time a transmission was scheduled; used
* for catching up v_t if no stations are eligible for
* transmission.
* @v_t: global virtual time; queues with v_t < this are eligible for
* transmission
* @weight_sum: total sum of all active stations used for dividing airtime
* @weight_sum_reciprocal: reciprocal of weight_sum (to avoid divisions in fast
* path - see comment above
* IEEE80211_RECIPROCAL_DIVISOR_64)
* @aql_txq_limit_low: AQL limit when total outstanding airtime
* is < IEEE80211_AQL_THRESHOLD
* @aql_txq_limit_high: AQL limit when total outstanding airtime
* is > IEEE80211_AQL_THRESHOLD
*/
struct airtime_sched_info {
spinlock_t lock;
struct rb_root_cached active_txqs;
struct rb_node *schedule_pos;
struct list_head active_list;
u64 last_weight_update;
u64 last_schedule_activity;
u64 v_t;
u64 weight_sum;
u64 weight_sum_reciprocal;
u32 aql_txq_limit_low;
u32 aql_txq_limit_high;
};
DECLARE_STATIC_KEY_FALSE(aql_disable);
struct ieee80211_local {
@ -1143,13 +1182,8 @@ struct ieee80211_local {
struct codel_params cparams;
/* protects active_txqs and txqi->schedule_order */
spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
struct list_head active_txqs[IEEE80211_NUM_ACS];
u16 schedule_round[IEEE80211_NUM_ACS];
struct airtime_sched_info airtime[IEEE80211_NUM_ACS];
u16 airtime_flags;
u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
u32 aql_threshold;
atomic_t aql_total_pending_airtime;
@ -1414,10 +1448,6 @@ struct ieee80211_local {
/* extended capabilities provided by mac80211 */
u8 ext_capa[8];
/* TDLS channel switch */
struct work_struct tdls_chsw_work;
struct sk_buff_head skb_queue_tdls_chsw;
};
static inline struct ieee80211_sub_if_data *
@ -1567,6 +1597,125 @@ static inline bool txq_has_queue(struct ieee80211_txq *txq)
return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets);
}
static inline struct airtime_info *to_airtime_info(struct ieee80211_txq *txq)
{
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
if (txq->sta) {
sta = container_of(txq->sta, struct sta_info, sta);
return &sta->airtime[txq->ac];
}
sdata = vif_to_sdata(txq->vif);
return &sdata->airtime[txq->ac];
}
/* To avoid divisions in the fast path, we keep pre-computed reciprocals for
* airtime weight calculations. There are two different weights to keep track
* of: The per-station weight and the sum of weights per phy.
*
* For the per-station weights (kept in airtime_info below), we use 32-bit
* reciprocals with a devisor of 2^19. This lets us keep the multiplications and
* divisions for the station weights as 32-bit operations at the cost of a bit
* of rounding error for high weights; but the choice of divisor keeps rounding
* errors <10% for weights <2^15, assuming no more than 8ms of airtime is
* reported at a time.
*
* For the per-phy sum of weights the values can get higher, so we use 64-bit
* operations for those with a 32-bit divisor, which should avoid any
* significant rounding errors.
*/
#define IEEE80211_RECIPROCAL_DIVISOR_64 0x100000000ULL
#define IEEE80211_RECIPROCAL_SHIFT_64 32
#define IEEE80211_RECIPROCAL_DIVISOR_32 0x80000U
#define IEEE80211_RECIPROCAL_SHIFT_32 19
static inline void airtime_weight_set(struct airtime_info *air_info, u16 weight)
{
if (air_info->weight == weight)
return;
air_info->weight = weight;
if (weight) {
air_info->weight_reciprocal =
IEEE80211_RECIPROCAL_DIVISOR_32 / weight;
} else {
air_info->weight_reciprocal = 0;
}
}
static inline void airtime_weight_sum_set(struct airtime_sched_info *air_sched,
int weight_sum)
{
if (air_sched->weight_sum == weight_sum)
return;
air_sched->weight_sum = weight_sum;
if (air_sched->weight_sum) {
air_sched->weight_sum_reciprocal = IEEE80211_RECIPROCAL_DIVISOR_64;
do_div(air_sched->weight_sum_reciprocal, air_sched->weight_sum);
} else {
air_sched->weight_sum_reciprocal = 0;
}
}
/* A problem when trying to enforce airtime fairness is that we want to divide
* the airtime between the currently *active* stations. However, basing this on
* the instantaneous queue state of stations doesn't work, as queues tend to
* oscillate very quickly between empty and occupied, leading to the scheduler
* thinking only a single station is active when deciding whether to allow
* transmission (and thus not throttling correctly).
*
* To fix this we use a timer-based notion of activity: a station is considered
* active if it has been scheduled within the last 100 ms; we keep a separate
* list of all the stations considered active in this manner, and lazily update
* the total weight of active stations from this list (filtering the stations in
* the list by their 'last active' time).
*
* We add one additional safeguard to guard against stations that manage to get
* scheduled every 100 ms but don't transmit a lot of data, and thus don't use
* up any airtime. Such stations would be able to get priority for an extended
* period of time if they do start transmitting at full capacity again, and so
* we add an explicit maximum for how far behind a station is allowed to fall in
* the virtual airtime domain. This limit is set to a relatively high value of
* 20 ms because the main mechanism for catching up idle stations is the active
* state as described above; i.e., the hard limit should only be hit in
* pathological cases.
*/
#define AIRTIME_ACTIVE_DURATION (100 * NSEC_PER_MSEC)
#define AIRTIME_MAX_BEHIND 20000 /* 20 ms */
static inline bool airtime_is_active(struct airtime_info *air_info, u64 now)
{
return air_info->last_scheduled >= now - AIRTIME_ACTIVE_DURATION;
}
static inline void airtime_set_active(struct airtime_sched_info *air_sched,
struct airtime_info *air_info, u64 now)
{
air_info->last_scheduled = now;
air_sched->last_schedule_activity = now;
list_move_tail(&air_info->list, &air_sched->active_list);
}
static inline bool airtime_catchup_v_t(struct airtime_sched_info *air_sched,
u64 v_t, u64 now)
{
air_sched->v_t = v_t;
return true;
}
static inline void init_airtime_info(struct airtime_info *air_info,
struct airtime_sched_info *air_sched)
{
atomic_set(&air_info->aql_tx_pending, 0);
air_info->aql_limit_low = air_sched->aql_txq_limit_low;
air_info->aql_limit_high = air_sched->aql_txq_limit_high;
airtime_weight_set(air_info, IEEE80211_DEFAULT_AIRTIME_WEIGHT);
INIT_LIST_HEAD(&air_info->list);
}
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
{
return ether_addr_equal(raddr, addr) ||
@ -1809,6 +1958,14 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
u64 *cookie);
int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len);
void ieee80211_resort_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq,
bool purge);
void ieee80211_update_airtime_weight(struct ieee80211_local *local,
struct airtime_sched_info *air_sched,
u64 now, bool force);
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
@ -1879,7 +2036,6 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta);
enum ieee80211_sta_rx_bandwidth
ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width);
enum nl80211_chan_width ieee80211_sta_cap_chan_bw(struct sta_info *sta);
void ieee80211_sta_set_rx_nss(struct sta_info *sta);
void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt);
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
@ -2287,9 +2443,13 @@ void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr);
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
void ieee80211_tdls_chsw_work(struct work_struct *wk);
void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
const u8 *peer, u16 reason);
void
ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
const char *ieee80211_get_reason_code_string(u16 reason_code);
u16 ieee80211_encode_usf(int val);
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,

View file

@ -1318,13 +1318,130 @@ static void ieee80211_if_setup_no_queue(struct net_device *dev)
dev->priv_flags |= IFF_NO_QUEUE;
}
static void ieee80211_iface_process_skb(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_mgmt *mgmt = (void *)skb->data;
if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
struct sta_info *sta;
int len = skb->len;
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta) {
switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ:
ieee80211_process_addba_request(local, sta,
mgmt, len);
break;
case WLAN_ACTION_ADDBA_RESP:
ieee80211_process_addba_resp(local, sta,
mgmt, len);
break;
case WLAN_ACTION_DELBA:
ieee80211_process_delba(sdata, sta,
mgmt, len);
break;
default:
WARN_ON(1);
break;
}
}
mutex_unlock(&local->sta_mtx);
} else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_VHT) {
switch (mgmt->u.action.u.vht_group_notif.action_code) {
case WLAN_VHT_ACTION_OPMODE_NOTIF: {
struct ieee80211_rx_status *status;
enum nl80211_band band;
struct sta_info *sta;
u8 opmode;
status = IEEE80211_SKB_RXCB(skb);
band = status->band;
opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta)
ieee80211_vht_handle_opmode(sdata, sta, opmode,
band);
mutex_unlock(&local->sta_mtx);
break;
}
case WLAN_VHT_ACTION_GROUPID_MGMT:
ieee80211_process_mu_groups(sdata, mgmt);
break;
default:
WARN_ON(1);
break;
}
} else if (ieee80211_is_ext(mgmt->frame_control)) {
if (sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_sta_rx_queued_ext(sdata, skb);
else
WARN_ON(1);
} else if (ieee80211_is_data_qos(mgmt->frame_control)) {
struct ieee80211_hdr *hdr = (void *)mgmt;
struct sta_info *sta;
/*
* So the frame isn't mgmt, but frame_control
* is at the right place anyway, of course, so
* the if statement is correct.
*
* Warn if we have other data frame types here,
* they must not get here.
*/
WARN_ON(hdr->frame_control &
cpu_to_le16(IEEE80211_STYPE_NULLFUNC));
WARN_ON(!(hdr->seq_ctrl &
cpu_to_le16(IEEE80211_SCTL_FRAG)));
/*
* This was a fragment of a frame, received while
* a block-ack session was active. That cannot be
* right, so terminate the session.
*/
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta) {
u16 tid = ieee80211_get_tid(hdr);
__ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_REQUIRE_SETUP,
true);
}
mutex_unlock(&local->sta_mtx);
} else switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
ieee80211_sta_rx_queued_mgmt(sdata, skb);
break;
case NL80211_IFTYPE_ADHOC:
ieee80211_ibss_rx_queued_mgmt(sdata, skb);
break;
case NL80211_IFTYPE_MESH_POINT:
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
ieee80211_mesh_rx_queued_mgmt(sdata, skb);
break;
default:
WARN(1, "frame for unexpected interface type");
break;
}
}
static void ieee80211_iface_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data, work);
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct sta_info *sta;
if (!ieee80211_sdata_running(sdata))
return;
@ -1337,116 +1454,12 @@ static void ieee80211_iface_work(struct work_struct *work)
/* first process frames */
while ((skb = skb_dequeue(&sdata->skb_queue))) {
struct ieee80211_mgmt *mgmt = (void *)skb->data;
kcov_remote_start_common(skb_get_kcov_handle(skb));
if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len;
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta) {
switch (mgmt->u.action.u.addba_req.action_code) {
case WLAN_ACTION_ADDBA_REQ:
ieee80211_process_addba_request(
local, sta, mgmt, len);
break;
case WLAN_ACTION_ADDBA_RESP:
ieee80211_process_addba_resp(local, sta,
mgmt, len);
break;
case WLAN_ACTION_DELBA:
ieee80211_process_delba(sdata, sta,
mgmt, len);
break;
default:
WARN_ON(1);
break;
}
}
mutex_unlock(&local->sta_mtx);
} else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_VHT) {
switch (mgmt->u.action.u.vht_group_notif.action_code) {
case WLAN_VHT_ACTION_OPMODE_NOTIF: {
struct ieee80211_rx_status *status;
enum nl80211_band band;
u8 opmode;
status = IEEE80211_SKB_RXCB(skb);
band = status->band;
opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta)
ieee80211_vht_handle_opmode(sdata, sta,
opmode,
band);
mutex_unlock(&local->sta_mtx);
break;
}
case WLAN_VHT_ACTION_GROUPID_MGMT:
ieee80211_process_mu_groups(sdata, mgmt);
break;
default:
WARN_ON(1);
break;
}
} else if (ieee80211_is_ext(mgmt->frame_control)) {
if (sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_sta_rx_queued_ext(sdata, skb);
else
WARN_ON(1);
} else if (ieee80211_is_data_qos(mgmt->frame_control)) {
struct ieee80211_hdr *hdr = (void *)mgmt;
/*
* So the frame isn't mgmt, but frame_control
* is at the right place anyway, of course, so
* the if statement is correct.
*
* Warn if we have other data frame types here,
* they must not get here.
*/
WARN_ON(hdr->frame_control &
cpu_to_le16(IEEE80211_STYPE_NULLFUNC));
WARN_ON(!(hdr->seq_ctrl &
cpu_to_le16(IEEE80211_SCTL_FRAG)));
/*
* This was a fragment of a frame, received while
* a block-ack session was active. That cannot be
* right, so terminate the session.
*/
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mgmt->sa);
if (sta) {
u16 tid = ieee80211_get_tid(hdr);
__ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_REQUIRE_SETUP,
true);
}
mutex_unlock(&local->sta_mtx);
} else switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
ieee80211_sta_rx_queued_mgmt(sdata, skb);
break;
case NL80211_IFTYPE_ADHOC:
ieee80211_ibss_rx_queued_mgmt(sdata, skb);
break;
case NL80211_IFTYPE_MESH_POINT:
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
ieee80211_mesh_rx_queued_mgmt(sdata, skb);
break;
default:
WARN(1, "frame for unexpected interface type");
break;
}
if (skb->protocol == cpu_to_be16(ETH_P_TDLS))
ieee80211_process_tdls_channel_switch(sdata, skb);
else
ieee80211_iface_process_skb(local, sdata, skb);
kfree_skb(skb);
kcov_remote_stop();
@ -1964,6 +1977,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
}
}
for (i = 0; i < IEEE80211_NUM_ACS; i++)
init_airtime_info(&sdata->airtime[i], &local->airtime[i]);
ieee80211_set_default_queues(sdata);
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;

View file

@ -259,7 +259,6 @@ static void tpt_trig_timer(struct timer_list *t)
{
struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer);
struct ieee80211_local *local = tpt_trig->local;
struct led_classdev *led_cdev;
unsigned long on, off, tpt;
int i;
@ -283,10 +282,7 @@ static void tpt_trig_timer(struct timer_list *t)
}
}
read_lock(&local->tpt_led.leddev_list_lock);
list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
led_blink_set(led_cdev, &on, &off);
read_unlock(&local->tpt_led.leddev_list_lock);
led_trigger_blink(&local->tpt_led, &on, &off);
}
const char *
@ -341,7 +337,6 @@ static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
{
struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
struct led_classdev *led_cdev;
if (!tpt_trig->running)
return;
@ -349,10 +344,7 @@ static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
tpt_trig->running = false;
del_timer_sync(&tpt_trig->timer);
read_lock(&local->tpt_led.leddev_list_lock);
list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
led_set_brightness(led_cdev, LED_OFF);
read_unlock(&local->tpt_led.leddev_list_lock);
led_trigger_event(&local->tpt_led, LED_OFF);
}
void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,

View file

@ -257,14 +257,13 @@ static void ieee80211_restart_work(struct work_struct *work)
/* wait for scan work complete */
flush_workqueue(local->workqueue);
flush_work(&local->sched_scan_stopped_work);
flush_work(&local->radar_detected_work);
rtnl_lock();
WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
"%s called with hardware scan in progress\n", __func__);
flush_work(&local->radar_detected_work);
/* we might do interface manipulations, so need both */
rtnl_lock();
wiphy_lock(local->hw.wiphy);
list_for_each_entry(sdata, &local->interfaces, list) {
/*
* XXX: there may be more work for other vif types and even
@ -706,10 +705,13 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
spin_lock_init(&local->queue_stop_reason_lock);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
INIT_LIST_HEAD(&local->active_txqs[i]);
spin_lock_init(&local->active_txq_lock[i]);
local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
local->aql_txq_limit_high[i] =
struct airtime_sched_info *air_sched = &local->airtime[i];
air_sched->active_txqs = RB_ROOT_CACHED;
INIT_LIST_HEAD(&air_sched->active_list);
spin_lock_init(&air_sched->lock);
air_sched->aql_txq_limit_low = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
air_sched->aql_txq_limit_high =
IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
}
@ -739,8 +741,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
INIT_WORK(&local->sched_scan_stopped_work,
ieee80211_sched_scan_stopped_work);
INIT_WORK(&local->tdls_chsw_work, ieee80211_tdls_chsw_work);
spin_lock_init(&local->ack_status_lock);
idr_init(&local->ack_status_frames);
@ -757,7 +757,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable);
skb_queue_head_init(&local->skb_queue_tdls_chsw);
ieee80211_alloc_led_names(local);
@ -1014,8 +1013,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
supp_ht = supp_ht || sband->ht_cap.ht_supported;
supp_vht = supp_vht || sband->vht_cap.vht_supported;
if (!supp_he)
supp_he = !!ieee80211_get_he_sta_cap(sband);
for (i = 0; i < sband->n_iftype_data; i++) {
const struct ieee80211_sband_iftype_data *iftd;
iftd = &sband->iftype_data[i];
supp_he = supp_he || (iftd && iftd->he_cap.has_he);
}
/* HT, VHT, HE require QoS, thus >= 4 queues */
if (WARN_ON(local->hw.queues < IEEE80211_NUM_ACS &&
@ -1389,7 +1393,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
cancel_delayed_work_sync(&local->roc_work);
cancel_work_sync(&local->restart_work);
cancel_work_sync(&local->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);
flush_work(&local->sched_scan_stopped_work);
flush_work(&local->radar_detected_work);
@ -1401,7 +1404,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable);
skb_queue_purge(&local->skb_queue_tdls_chsw);
wiphy_unregister(local->hw.wiphy);
destroy_workqueue(local->workqueue);

View file

@ -134,7 +134,7 @@ struct mesh_path {
* gate's mpath may or may not be resolved and active.
* @gates_lock: protects updates to known_gates
* @rhead: the rhashtable containing struct mesh_paths, keyed by dest addr
* @walk_head: linked list containging all mesh_path objects
* @walk_head: linked list containing all mesh_path objects
* @walk_lock: lock protecting walk_head
* @entries: number of entries in the table
*/

View file

@ -1124,7 +1124,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
* forwarding information is found.
*
* Returns: 0 if the next hop was found and -ENOENT if the frame was queued.
* skb is freeed here if no mpath could be allocated.
* skb is freed here if no mpath could be allocated.
*/
int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)

View file

@ -122,7 +122,7 @@ static void prepare_for_gate(struct sk_buff *skb, char *dst_addr,
hdr = (struct ieee80211_hdr *) skb->data;
/* we preserve the previous mesh header and only add
* the new addreses */
* the new addresses */
mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
mshdr->flags = MESH_FLAGS_AE_A5_A6;
memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN);

View file

@ -150,7 +150,7 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
* mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT
* mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is
* selected if any non-HT peers are present in our MBSS. 20MHz-protection mode
* is selected if all peers in our 20/40MHz MBSS support HT and atleast one
* is selected if all peers in our 20/40MHz MBSS support HT and at least one
* HT20 peer is present. Otherwise no-protection mode is selected.
*/
static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)

View file

@ -8,7 +8,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
* Copyright (C) 2018 - 2020 Intel Corporation
* Copyright (C) 2018 - 2021 Intel Corporation
*/
#include <linux/delay.h>
@ -371,7 +371,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def chandef;
u16 ht_opmode;
u32 flags;
enum ieee80211_sta_rx_bandwidth new_sta_bw;
u32 vht_cap_info = 0;
int ret;
@ -385,7 +384,9 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
/* don't check HE if we associated as non-HE station */
if (ifmgd->flags & IEEE80211_STA_DISABLE_HE ||
!ieee80211_get_he_sta_cap(sband))
!ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif)))
he_oper = NULL;
if (WARN_ON_ONCE(!sta))
@ -445,40 +446,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
IEEE80211_STA_DISABLE_160MHZ)) ||
!cfg80211_chandef_valid(&chandef)) {
sdata_info(sdata,
"AP %pM changed bandwidth in a way we can't support - disconnect\n",
ifmgd->bssid);
"AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n",
ifmgd->bssid, flags, ifmgd->flags);
return -EINVAL;
}
switch (chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
new_sta_bw = IEEE80211_STA_RX_BW_20;
break;
case NL80211_CHAN_WIDTH_40:
new_sta_bw = IEEE80211_STA_RX_BW_40;
break;
case NL80211_CHAN_WIDTH_80:
new_sta_bw = IEEE80211_STA_RX_BW_80;
break;
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
new_sta_bw = IEEE80211_STA_RX_BW_160;
break;
default:
return -EINVAL;
}
if (new_sta_bw > sta->cur_max_bandwidth)
new_sta_bw = sta->cur_max_bandwidth;
if (new_sta_bw < sta->sta.bandwidth) {
sta->sta.bandwidth = new_sta_bw;
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_BW_CHANGED);
}
ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed);
if (ret) {
sdata_info(sdata,
"AP %pM changed bandwidth to incompatible one - disconnect\n",
@ -486,12 +460,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
return ret;
}
if (new_sta_bw > sta->sta.bandwidth) {
sta->sta.bandwidth = new_sta_bw;
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_BW_CHANGED);
}
return 0;
}
@ -617,7 +585,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
/*
* If some other vif is using the MU-MIMO capablity we cannot associate
* If some other vif is using the MU-MIMO capability we cannot associate
* using MU-MIMO - this will lead to contradictions in the group-id
* mechanism.
* Ownership is defined since association request, in order to avoid
@ -676,7 +644,8 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
he_cap = ieee80211_get_he_sta_cap(sband);
he_cap = ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif));
if (!he_cap || !reg_cap)
return;
@ -712,6 +681,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
u32 rates = 0;
__le16 listen_int;
struct element *ext_capa = NULL;
enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
const struct ieee80211_sband_iftype_data *iftd;
struct ieee80211_prep_tx_info info = {};
/* we know it's writable, cast away the const */
if (assoc_data->ie_len)
@ -756,6 +728,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
}
iftd = ieee80211_get_sband_iftype_data(sband, iftype);
skb = alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + /* bit too much but doesn't matter */
2 + assoc_data->ssid_len + /* SSID */
@ -770,7 +744,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
assoc_data->ie_len + /* extra IEs */
(assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) +
9, /* WMM */
9 + /* WMM */
(iftd ? iftd->vendor_elems.len : 0),
GFP_KERNEL);
if (!skb)
return;
@ -810,12 +785,14 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
mgmt->u.reassoc_req.listen_interval = listen_int;
memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid,
ETH_ALEN);
info.subtype = IEEE80211_STYPE_REASSOC_REQ;
} else {
skb_put(skb, 4);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
mgmt->u.assoc_req.listen_interval = listen_int;
info.subtype = IEEE80211_STYPE_ASSOC_REQ;
}
/* SSID */
@ -1043,6 +1020,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb);
}
if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len)
skb_put_data(skb, iftd->vendor_elems.data, iftd->vendor_elems.len);
/* add any remaining custom (i.e. vendor specific here) IEs */
if (assoc_data->ie_len) {
noffset = assoc_data->ie_len;
@ -1060,7 +1040,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
ifmgd->assoc_req_ies = kmemdup(ie_start, pos - ie_start, GFP_ATOMIC);
ifmgd->assoc_req_ies_len = pos - ie_start;
drv_mgd_prepare_tx(local, sdata, 0);
drv_mgd_prepare_tx(local, sdata, &info);
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
@ -1094,11 +1074,6 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
struct ieee80211_hdr_3addr *nullfunc;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
/* Don't send NDPs when STA is connected HE */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
return;
skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif,
!ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP));
if (!skb)
@ -1130,10 +1105,6 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return;
/* Don't send NDPs when connected HE */
if (!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE))
return;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
if (!skb)
return;
@ -1183,10 +1154,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
*/
if (sdata->reserved_chanctx) {
struct ieee80211_supported_band *sband = NULL;
struct sta_info *mgd_sta = NULL;
enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_20;
/*
* with multi-vif csa driver may call ieee80211_csa_finish()
* many times while waiting for other interfaces to use their
@ -1195,48 +1162,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (sdata->reserved_ready)
goto out;
if (sdata->vif.bss_conf.chandef.width !=
sdata->csa_chandef.width) {
/*
* For managed interface, we need to also update the AP
* station bandwidth and align the rate scale algorithm
* on the bandwidth change. Here we only consider the
* bandwidth of the new channel definition (as channel
* switch flow does not have the full HT/VHT/HE
* information), assuming that if additional changes are
* required they would be done as part of the processing
* of the next beacon from the AP.
*/
switch (sdata->csa_chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
default:
bw = IEEE80211_STA_RX_BW_20;
break;
case NL80211_CHAN_WIDTH_40:
bw = IEEE80211_STA_RX_BW_40;
break;
case NL80211_CHAN_WIDTH_80:
bw = IEEE80211_STA_RX_BW_80;
break;
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
bw = IEEE80211_STA_RX_BW_160;
break;
}
mgd_sta = sta_info_get(sdata, ifmgd->bssid);
sband =
local->hw.wiphy->bands[sdata->csa_chandef.chan->band];
}
if (sdata->vif.bss_conf.chandef.width >
sdata->csa_chandef.width) {
mgd_sta->sta.bandwidth = bw;
rate_control_rate_update(local, sband, mgd_sta,
IEEE80211_RC_BW_CHANGED);
}
ret = ieee80211_vif_use_reserved_context(sdata);
if (ret) {
sdata_info(sdata,
@ -1247,13 +1172,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
goto out;
}
if (sdata->vif.bss_conf.chandef.width <
sdata->csa_chandef.width) {
mgd_sta->sta.bandwidth = bw;
rate_control_rate_update(local, sband, mgd_sta,
IEEE80211_RC_BW_CHANGED);
}
goto out;
}
@ -2341,6 +2259,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
u32 changed = 0;
struct ieee80211_prep_tx_info info = {
.subtype = stype,
};
sdata_assert_lock(sdata);
@ -2390,8 +2311,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
* driver requested so.
*/
if (ieee80211_hw_check(&local->hw, DEAUTH_NEED_MGD_TX_PREP) &&
!ifmgd->have_beacon)
drv_mgd_prepare_tx(sdata->local, sdata, 0);
!ifmgd->have_beacon) {
drv_mgd_prepare_tx(sdata->local, sdata, &info);
}
ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid,
ifmgd->bssid, stype, reason,
@ -2402,6 +2324,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
if (tx)
ieee80211_flush_queues(local, sdata, false);
drv_mgd_complete_tx(sdata->local, sdata, &info);
/* clear bssid only after building the needed mgmt frames */
eth_zero_addr(ifmgd->bssid);
@ -2617,10 +2541,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
ifmgd->nullfunc_failed = false;
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
ifmgd->probe_send_count--;
else
ieee80211_send_nullfunc(sdata->local, sdata, false);
ieee80211_send_nullfunc(sdata->local, sdata, false);
} else {
int ssid_len;
@ -2952,6 +2873,9 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
u8 *pos;
struct ieee802_11_elems elems;
u32 tx_flags = 0;
struct ieee80211_prep_tx_info info = {
.subtype = IEEE80211_STYPE_AUTH,
};
pos = mgmt->u.auth.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
@ -2959,7 +2883,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
if (!elems.challenge)
return;
auth_data->expected_transaction = 4;
drv_mgd_prepare_tx(sdata->local, sdata, 0);
drv_mgd_prepare_tx(sdata->local, sdata, &info);
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
IEEE80211_TX_INTFL_MLME_CONN_TX;
@ -3012,6 +2936,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
.type = MLME_EVENT,
.u.mlme.data = AUTH_EVENT,
};
struct ieee80211_prep_tx_info info = {
.subtype = IEEE80211_STYPE_AUTH,
};
sdata_assert_lock(sdata);
@ -3040,7 +2967,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
auth_transaction,
ifmgd->auth_data->expected_transaction);
return;
goto notify_driver;
}
if (status_code != WLAN_STATUS_SUCCESS) {
@ -3051,7 +2978,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
(auth_transaction == 1 &&
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK))))
return;
goto notify_driver;
sdata_info(sdata, "%pM denied authentication (status %d)\n",
mgmt->sa, status_code);
@ -3059,7 +2986,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
event.u.mlme.status = MLME_DENIED;
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
return;
goto notify_driver;
}
switch (ifmgd->auth_data->algorithm) {
@ -3081,10 +3008,11 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
default:
WARN_ONCE(1, "invalid auth alg %d",
ifmgd->auth_data->algorithm);
return;
goto notify_driver;
}
event.u.mlme.status = MLME_SUCCESS;
info.success = 1;
drv_event_callback(sdata->local, sdata, &event);
if (ifmgd->auth_data->algorithm != WLAN_AUTH_SAE ||
(auth_transaction == 2 &&
@ -3098,6 +3026,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
}
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
notify_driver:
drv_mgd_complete_tx(sdata->local, sdata, &info);
}
#define case_WLAN(type) \
@ -3314,6 +3244,23 @@ static int ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata,
return 0;
}
static bool ieee80211_twt_bcast_support(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *bss_conf,
struct ieee80211_supported_band *sband,
struct sta_info *sta)
{
const struct ieee80211_sta_he_cap *own_he_cap =
ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif));
return bss_conf->he_support &&
(sta->sta.he_cap.he_cap_elem.mac_cap_info[2] &
IEEE80211_HE_MAC_CAP2_BCAST_TWT) &&
own_he_cap &&
(own_he_cap->he_cap_elem.mac_cap_info[2] &
IEEE80211_HE_MAC_CAP2_BCAST_TWT);
}
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss,
struct ieee80211_mgmt *mgmt, size_t len,
@ -3529,6 +3476,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
bss_conf->twt_protected = false;
}
bss_conf->twt_broadcast =
ieee80211_twt_bcast_support(sdata, bss_conf, sband, sta);
if (bss_conf->he_support) {
bss_conf->he_bss_color.color =
le32_get_bits(elems->he_operation->he_oper_params,
@ -3699,6 +3649,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
.type = MLME_EVENT,
.u.mlme.data = ASSOC_EVENT,
};
struct ieee80211_prep_tx_info info = {};
sdata_assert_lock(sdata);
@ -3728,6 +3679,15 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
aid = 0; /* TODO */
}
/*
* Note: this may not be perfect, AP might misbehave - if
* anyone needs to rely on perfect complete notification
* with the exact right subtype, then we need to track what
* we actually transmitted.
*/
info.subtype = reassoc ? IEEE80211_STYPE_REASSOC_REQ :
IEEE80211_STYPE_ASSOC_REQ;
sdata_info(sdata,
"RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
reassoc ? "Rea" : "A", mgmt->sa,
@ -3753,7 +3713,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
assoc_data->timeout_started = true;
if (ms > IEEE80211_ASSOC_TIMEOUT)
run_again(sdata, assoc_data->timeout);
return;
goto notify_driver;
}
if (status_code != WLAN_STATUS_SUCCESS) {
@ -3768,7 +3728,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
/* oops -- internal error -- send timeout for now */
ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, cbss);
return;
goto notify_driver;
}
event.u.mlme.status = MLME_SUCCESS;
drv_event_callback(sdata->local, sdata, &event);
@ -3786,10 +3746,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
if (sdata->tx_conf[ac].uapsd)
uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
info.success = 1;
}
cfg80211_rx_assoc_resp(sdata->dev, cbss, (u8 *)mgmt, len, uapsd_queues,
ifmgd->assoc_req_ies, ifmgd->assoc_req_ies_len);
notify_driver:
drv_mgd_complete_tx(sdata->local, sdata, &info);
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@ -4408,7 +4372,9 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
u32 tx_flags = 0;
u16 trans = 1;
u16 status = 0;
u16 prepare_tx_duration = 0;
struct ieee80211_prep_tx_info info = {
.subtype = IEEE80211_STYPE_AUTH,
};
sdata_assert_lock(sdata);
@ -4431,10 +4397,9 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
}
if (auth_data->algorithm == WLAN_AUTH_SAE)
prepare_tx_duration =
jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE);
info.duration = jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE);
drv_mgd_prepare_tx(local, sdata, prepare_tx_duration);
drv_mgd_prepare_tx(local, sdata, &info);
sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
auth_data->bss->bssid, auth_data->tries,
@ -4929,11 +4894,13 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
}
static bool
ieee80211_verify_sta_he_mcs_support(struct ieee80211_supported_band *sband,
ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const struct ieee80211_he_operation *he_op)
{
const struct ieee80211_sta_he_cap *sta_he_cap =
ieee80211_get_he_sta_cap(sband);
ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif));
u16 ap_min_req_set;
int i;
@ -5027,7 +4994,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
}
if (!ieee80211_get_he_sta_cap(sband))
if (!ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif)))
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
rcu_read_lock();
@ -5085,7 +5053,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
else
he_oper = NULL;
if (!ieee80211_verify_sta_he_mcs_support(sband, he_oper))
if (!ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper))
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
}
@ -5655,15 +5623,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
2 * FILS_NONCE_LEN);
assoc_data->bss = req->bss;
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
if (ifmgd->powersave)
sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
else
sdata->smps_mode = IEEE80211_SMPS_OFF;
} else
sdata->smps_mode = ifmgd->req_smps;
assoc_data->capability = req->bss->capability;
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
@ -5770,6 +5729,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (err)
goto err_clear;
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
if (ifmgd->powersave)
sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
else
sdata->smps_mode = IEEE80211_SMPS_OFF;
} else {
sdata->smps_mode = ifmgd->req_smps;
}
rcu_read_lock();
beacon_ies = rcu_dereference(req->bss->beacon_ies);
@ -5854,6 +5822,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change;
struct ieee80211_prep_tx_info info = {
.subtype = IEEE80211_STYPE_DEAUTH,
};
if (ifmgd->auth_data &&
ether_addr_equal(ifmgd->auth_data->bss->bssid, req->bssid)) {
@ -5862,7 +5833,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
req->bssid, req->reason_code,
ieee80211_get_reason_code_string(req->reason_code));
drv_mgd_prepare_tx(sdata->local, sdata, 0);
drv_mgd_prepare_tx(sdata->local, sdata, &info);
ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
@ -5871,7 +5842,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_report_disconnect(sdata, frame_buf,
sizeof(frame_buf), true,
req->reason_code, false);
drv_mgd_complete_tx(sdata->local, sdata, &info);
return 0;
}
@ -5882,7 +5853,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
req->bssid, req->reason_code,
ieee80211_get_reason_code_string(req->reason_code));
drv_mgd_prepare_tx(sdata->local, sdata, 0);
drv_mgd_prepare_tx(sdata->local, sdata, &info);
ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
@ -5906,6 +5877,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_report_disconnect(sdata, frame_buf,
sizeof(frame_buf), true,
req->reason_code, false);
drv_mgd_complete_tx(sdata->local, sdata, &info);
return 0;
}

View file

@ -297,15 +297,11 @@ void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata)
static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
{
struct sk_buff *skb = txrc->skb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
__le16 fc;
fc = hdr->frame_control;
return (info->flags & (IEEE80211_TX_CTL_NO_ACK |
IEEE80211_TX_CTL_USE_MINRATE)) ||
!ieee80211_is_data(fc);
!ieee80211_is_tx_data(skb);
}
static void rc_send_low_basicrate(struct ieee80211_tx_rate *rate,
@ -396,6 +392,10 @@ static bool rate_control_send_low(struct ieee80211_sta *pubsta,
int mcast_rate;
bool use_basicrate = false;
if (ieee80211_is_tx_data(txrc->skb) &&
info->flags & IEEE80211_TX_CTL_NO_ACK)
return false;
if (!pubsta || rc_no_data_or_no_ack_use_min(txrc)) {
__rate_control_send_low(txrc->hw, sband, pubsta, info,
txrc->rate_idx_mask);
@ -870,7 +870,6 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
int max_rates)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_supported_band *sband;
@ -882,7 +881,7 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
sdata = vif_to_sdata(vif);
sband = sdata->local->hw.wiphy->bands[info->band];
if (ieee80211_is_data(hdr->frame_control))
if (ieee80211_is_tx_data(skb))
rate_control_apply_mask(sdata, sta, sband, dest, max_rates);
if (dest[0].idx < 0)

View file

@ -434,7 +434,7 @@ minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
unsigned int nsecs = 0, overhead = mi->overhead;
unsigned int ampdu_len = 1;
/* do not account throughput if sucess prob is below 10% */
/* do not account throughput if success prob is below 10% */
if (prob_avg < MINSTREL_FRAC(10, 100))
return 0;
@ -1175,29 +1175,6 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
}
}
static void
minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
u16 tid;
if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
return;
if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
return;
if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
return;
tid = ieee80211_get_tid(hdr);
if (likely(sta->ampdu_mlme.tid_tx[tid]))
return;
ieee80211_start_tx_ba_session(pubsta, tid, 0);
}
static void
minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
void *priv_sta, struct ieee80211_tx_status *st)
@ -1211,6 +1188,10 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
bool last, update = false;
int i;
/* Ignore packet that was sent with noAck flag */
if (info->flags & IEEE80211_TX_CTL_NO_ACK)
return;
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU))
@ -1498,10 +1479,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
struct minstrel_priv *mp = priv;
u16 sample_idx;
if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
!minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate)))
minstrel_aggr_check(sta, txrc->skb);
info->flags |= mi->tx_flags;
#ifdef CONFIG_MAC80211_DEBUGFS
@ -1907,6 +1884,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
static const struct rate_control_ops mac80211_minstrel_ht = {
.name = "minstrel_ht",
.capa = RATE_CTRL_CAPA_AMPDU_TRIGGER,
.tx_status_ext = minstrel_ht_tx_status,
.get_rate = minstrel_ht_get_rate,
.rate_init = minstrel_ht_rate_init,

View file

@ -214,6 +214,24 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
return len;
}
static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct sk_buff *skb)
{
skb_queue_tail(&sdata->skb_queue, skb);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
if (sta)
sta->rx_stats.packets++;
}
static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct sk_buff *skb)
{
skb->protocol = 0;
__ieee80211_queue_skb_to_iface(sdata, sta, skb);
}
static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
int rtap_space)
@ -254,8 +272,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
if (!skb)
return;
skb_queue_tail(&sdata->skb_queue, skb);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
ieee80211_queue_skb_to_iface(sdata, NULL, skb);
}
/*
@ -1339,7 +1356,6 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
struct sk_buff_head *frames)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_local *local = rx->local;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta = rx->sta;
struct tid_ampdu_rx *tid_agg_rx;
@ -1391,8 +1407,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
skb_queue_tail(&rx->sdata->skb_queue, skb);
ieee80211_queue_work(&local->hw, &rx->sdata->work);
ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb);
return;
}
@ -1563,12 +1578,8 @@ static void sta_ps_start(struct sta_info *sta)
for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
struct ieee80211_txq *txq = sta->sta.txq[tid];
struct txq_info *txqi = to_txq_info(txq);
spin_lock(&local->active_txq_lock[txq->ac]);
if (!list_empty(&txqi->schedule_order))
list_del_init(&txqi->schedule_order);
spin_unlock(&local->active_txq_lock[txq->ac]);
ieee80211_unschedule_txq(&local->hw, txq, false);
if (txq_has_queue(txq))
set_bit(tid, &sta->txq_buffered_tids);
@ -3009,11 +3020,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
tf->category == WLAN_CATEGORY_TDLS &&
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
schedule_work(&local->tdls_chsw_work);
if (rx->sta)
rx->sta->rx_stats.packets++;
rx->skb->protocol = cpu_to_be16(ETH_P_TDLS);
__ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
return RX_QUEUED;
}
}
@ -3493,10 +3501,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_QUEUED;
queue:
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&local->hw, &sdata->work);
if (rx->sta)
rx->sta->rx_stats.packets++;
ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
return RX_QUEUED;
}
@ -3644,10 +3649,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
/* for now only beacons are ext, so queue them */
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&rx->local->hw, &sdata->work);
if (rx->sta)
rx->sta->rx_stats.packets++;
ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
return RX_QUEUED;
}
@ -3704,11 +3706,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
}
/* queue up frame and kick off work to process it */
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&rx->local->hw, &sdata->work);
if (rx->sta)
rx->sta->rx_stats.packets++;
ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
return RX_QUEUED;
}

View file

@ -425,15 +425,11 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
if (sta_prepare_rate_control(local, sta, gfp))
goto free_txq;
sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
skb_queue_head_init(&sta->ps_tx_buf[i]);
skb_queue_head_init(&sta->tx_filtered[i]);
sta->airtime[i].deficit = sta->airtime_weight;
atomic_set(&sta->airtime[i].aql_tx_pending, 0);
sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i];
sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i];
init_airtime_info(&sta->airtime[i], &local->airtime[i]);
}
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
@ -1398,11 +1394,6 @@ static void ieee80211_send_null_response(struct sta_info *sta, int tid,
struct ieee80211_tx_info *info;
struct ieee80211_chanctx_conf *chanctx_conf;
/* Don't send NDPs when STA is connected HE */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE))
return;
if (qos) {
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
@ -1897,24 +1888,59 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
}
EXPORT_SYMBOL(ieee80211_sta_set_buffered);
void ieee80211_register_airtime(struct ieee80211_txq *txq,
u32 tx_airtime, u32 rx_airtime)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
struct ieee80211_local *local = sdata->local;
u64 weight_sum, weight_sum_reciprocal;
struct airtime_sched_info *air_sched;
struct airtime_info *air_info;
u32 airtime = 0;
air_sched = &local->airtime[txq->ac];
air_info = to_airtime_info(txq);
if (local->airtime_flags & AIRTIME_USE_TX)
airtime += tx_airtime;
if (local->airtime_flags & AIRTIME_USE_RX)
airtime += rx_airtime;
/* Weights scale so the unit weight is 256 */
airtime <<= 8;
spin_lock_bh(&air_sched->lock);
air_info->tx_airtime += tx_airtime;
air_info->rx_airtime += rx_airtime;
if (air_sched->weight_sum) {
weight_sum = air_sched->weight_sum;
weight_sum_reciprocal = air_sched->weight_sum_reciprocal;
} else {
weight_sum = air_info->weight;
weight_sum_reciprocal = air_info->weight_reciprocal;
}
/* Round the calculation of global vt */
air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) *
weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64;
air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) *
air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32;
ieee80211_resort_txq(&local->hw, txq);
spin_unlock_bh(&air_sched->lock);
}
void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
u32 tx_airtime, u32 rx_airtime)
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
struct ieee80211_local *local = sta->sdata->local;
u8 ac = ieee80211_ac_from_tid(tid);
u32 airtime = 0;
struct ieee80211_txq *txq = pubsta->txq[tid];
if (sta->local->airtime_flags & AIRTIME_USE_TX)
airtime += tx_airtime;
if (sta->local->airtime_flags & AIRTIME_USE_RX)
airtime += rx_airtime;
if (!txq)
return;
spin_lock_bh(&local->active_txq_lock[ac]);
sta->airtime[ac].tx_airtime += tx_airtime;
sta->airtime[ac].rx_airtime += rx_airtime;
sta->airtime[ac].deficit -= airtime;
spin_unlock_bh(&local->active_txq_lock[ac]);
ieee80211_register_airtime(txq, tx_airtime, rx_airtime);
}
EXPORT_SYMBOL(ieee80211_sta_register_airtime);
@ -2093,10 +2119,9 @@ static struct ieee80211_sta_rx_stats *
sta_get_last_rx_stats(struct sta_info *sta)
{
struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
struct ieee80211_local *local = sta->local;
int cpu;
if (!ieee80211_hw_check(&local->hw, USES_RSS))
if (!sta->pcpu_rx_stats)
return stats;
for_each_possible_cpu(cpu) {
@ -2196,9 +2221,7 @@ static void sta_set_tidstats(struct sta_info *sta,
int cpu;
if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
if (!ieee80211_hw_check(&local->hw, USES_RSS))
tidstats->rx_msdu +=
sta_get_tidstats_msdu(&sta->rx_stats, tid);
tidstats->rx_msdu += sta_get_tidstats_msdu(&sta->rx_stats, tid);
if (sta->pcpu_rx_stats) {
for_each_possible_cpu(cpu) {
@ -2277,7 +2300,6 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
sinfo->rx_beacon = sdata->u.mgd.count_beacon_signal;
drv_sta_statistics(local, sdata, &sta->sta, sinfo);
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) |
BIT_ULL(NL80211_STA_INFO_STA_FLAGS) |
BIT_ULL(NL80211_STA_INFO_BSS_PARAM) |
@ -2312,8 +2334,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
if (!(sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES64) |
BIT_ULL(NL80211_STA_INFO_RX_BYTES)))) {
if (!ieee80211_hw_check(&local->hw, USES_RSS))
sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats);
sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats);
if (sta->pcpu_rx_stats) {
for_each_possible_cpu(cpu) {
@ -2363,7 +2384,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
}
if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
sinfo->airtime_weight = sta->airtime_weight;
sinfo->airtime_weight = sta->airtime[0].weight;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
}

View file

@ -135,18 +135,25 @@ enum ieee80211_agg_stop_reason {
#define AIRTIME_USE_TX BIT(0)
#define AIRTIME_USE_RX BIT(1)
struct airtime_info {
u64 rx_airtime;
u64 tx_airtime;
s64 deficit;
u64 v_t;
u64 last_scheduled;
struct list_head list;
atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
u32 aql_limit_low;
u32 aql_limit_high;
u32 weight_reciprocal;
u16 weight;
};
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
struct sta_info *sta, u8 ac,
u16 tx_airtime, bool tx_completed);
void ieee80211_register_airtime(struct ieee80211_txq *txq,
u32 tx_airtime, u32 rx_airtime);
struct sta_info;
@ -515,7 +522,6 @@ struct ieee80211_fragment_cache {
* @tid_seq: per-TID sequence numbers for sending to this STA
* @airtime: per-AC struct airtime_info describing airtime statistics for this
* station
* @airtime_weight: station weight for airtime fairness calculation purposes
* @ampdu_mlme: A-MPDU state machine state
* @mesh: mesh STA information
* @debugfs_dir: debug filesystem directory dentry
@ -646,7 +652,6 @@ struct sta_info {
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
struct airtime_info airtime[IEEE80211_NUM_ACS];
u16 airtime_weight;
/*
* Aggregation information, locked with lock.

View file

@ -970,6 +970,25 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
ieee80211_frame_acked(sta, skb);
} else if (wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_txq *txq;
u32 airtime;
/* Account airtime to multicast queue */
sdata = ieee80211_sdata_from_skb(local, skb);
if (sdata && (txq = sdata->vif.txq)) {
airtime = info->status.tx_time ?:
ieee80211_calc_expected_tx_airtime(hw,
&sdata->vif,
NULL,
skb->len,
false);
ieee80211_register_airtime(txq, airtime, 0);
}
}
/* SNMP counters
@ -1006,12 +1025,11 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) &&
!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
local->ps_sdata && !(local->scanning)) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
if (info->flags & IEEE80211_TX_STAT_ACK)
local->ps_sdata->u.mgd.flags |=
IEEE80211_STA_NULLFUNC_ACKED;
} else
mod_timer(&local->dynamic_ps_timer, jiffies +
msecs_to_jiffies(10));
mod_timer(&local->dynamic_ps_timer,
jiffies + msecs_to_jiffies(10));
}
ieee80211_report_used_skb(local, skb, false);

View file

@ -1920,7 +1920,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
return ret;
}
static void
void
ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
@ -1971,32 +1971,6 @@ void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
rcu_read_unlock();
}
void ieee80211_tdls_chsw_work(struct work_struct *wk)
{
struct ieee80211_local *local =
container_of(wk, struct ieee80211_local, tdls_chsw_work);
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
struct ieee80211_tdls_data *tf;
wiphy_lock(local->hw.wiphy);
while ((skb = skb_dequeue(&local->skb_queue_tdls_chsw))) {
tf = (struct ieee80211_tdls_data *)skb->data;
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata) ||
sdata->vif.type != NL80211_IFTYPE_STATION ||
!ether_addr_equal(tf->da, sdata->vif.addr))
continue;
ieee80211_process_tdls_channel_switch(sdata, skb);
break;
}
kfree_skb(skb);
}
wiphy_unlock(local->hw.wiphy);
}
void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
const u8 *peer, u16 reason)
{

View file

@ -2,7 +2,7 @@
/*
* Portions of this file
* Copyright(c) 2016-2017 Intel Deutschland GmbH
* Copyright (C) 2018 - 2020 Intel Corporation
* Copyright (C) 2018 - 2021 Intel Corporation
*/
#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
@ -1461,31 +1461,52 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames,
TP_ARGS(local, sta, tids, num_frames, reason, more_data)
);
TRACE_EVENT(drv_mgd_prepare_tx,
DECLARE_EVENT_CLASS(mgd_prepare_complete_tx_evt,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 duration),
u16 duration, u16 subtype, bool success),
TP_ARGS(local, sdata, duration),
TP_ARGS(local, sdata, duration, subtype, success),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u32, duration)
__field(u16, subtype)
__field(u8, success)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->duration = duration;
__entry->subtype = subtype;
__entry->success = success;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT " duration: %u",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->duration
LOCAL_PR_FMT VIF_PR_FMT " duration: %u, subtype:0x%x, success:%d",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->duration,
__entry->subtype, __entry->success
)
);
DEFINE_EVENT(mgd_prepare_complete_tx_evt, drv_mgd_prepare_tx,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 duration, u16 subtype, bool success),
TP_ARGS(local, sdata, duration, subtype, success)
);
DEFINE_EVENT(mgd_prepare_complete_tx_evt, drv_mgd_complete_tx,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u16 duration, u16 subtype, bool success),
TP_ARGS(local, sdata, duration, subtype, success)
);
DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),

View file

@ -18,6 +18,7 @@
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
#include <linux/timekeeping.h>
#include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
@ -666,6 +667,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
u32 len;
struct ieee80211_tx_rate_control txrc;
struct ieee80211_sta_rates *ratetbl = NULL;
bool encap = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
bool assoc = false;
memset(&txrc, 0, sizeof(txrc));
@ -707,7 +709,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
* just wants a probe response.
*/
if (tx->sdata->vif.bss_conf.use_short_preamble &&
(ieee80211_is_data(hdr->frame_control) ||
(ieee80211_is_tx_data(tx->skb) ||
(tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
txrc.short_preamble = true;
@ -729,7 +731,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
"%s: Dropped data frame as no usable bitrate found while "
"scanning and associated. Target station: "
"%pM on %d GHz band\n",
tx->sdata->name, hdr->addr1,
tx->sdata->name,
encap ? ((struct ethhdr *)hdr)->h_dest : hdr->addr1,
info->band ? 5 : 2))
return TX_DROP;
@ -763,7 +766,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
if (txrc.reported_rate.idx < 0) {
txrc.reported_rate = tx->rate;
if (tx->sta && ieee80211_is_data(hdr->frame_control))
if (tx->sta && ieee80211_is_tx_data(tx->skb))
tx->sta->tx_stats.last_rate = txrc.reported_rate;
} else if (tx->sta)
tx->sta->tx_stats.last_rate = txrc.reported_rate;
@ -1447,7 +1450,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
codel_vars_init(&txqi->def_cvars);
codel_stats_init(&txqi->cstats);
__skb_queue_head_init(&txqi->frags);
INIT_LIST_HEAD(&txqi->schedule_order);
RB_CLEAR_NODE(&txqi->schedule_order);
txqi->txq.vif = &sdata->vif;
@ -1491,9 +1494,7 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
spin_unlock_bh(&fq->lock);
spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
list_del_init(&txqi->schedule_order);
spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
ieee80211_unschedule_txq(&local->hw, &txqi->txq, true);
}
void ieee80211_txq_set_params(struct ieee80211_local *local)
@ -1768,8 +1769,6 @@ static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
CALL_TXH(ieee80211_tx_h_ps_buf);
CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
CALL_TXH(ieee80211_tx_h_select_key);
if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
CALL_TXH(ieee80211_tx_h_rate_ctrl);
txh_done:
if (unlikely(res == TX_DROP)) {
@ -1802,6 +1801,9 @@ static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
goto txh_done;
}
if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
CALL_TXH(ieee80211_tx_h_rate_ctrl);
CALL_TXH(ieee80211_tx_h_michael_mic_add);
CALL_TXH(ieee80211_tx_h_sequence);
CALL_TXH(ieee80211_tx_h_fragment);
@ -3284,6 +3286,9 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
return false;
if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
return false;
if (skb_is_gso(skb))
return false;
@ -3389,15 +3394,21 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
* Can be called while the sta lock is held. Anything that can cause packets to
* be generated will cause deadlock!
*/
static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 pn_offs,
struct ieee80211_key *key,
struct sk_buff *skb)
static ieee80211_tx_result
ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 pn_offs,
struct ieee80211_key *key,
struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
u8 tid = IEEE80211_NUM_TIDS;
if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL) &&
ieee80211_tx_h_rate_ctrl(tx) != TX_CONTINUE)
return TX_DROP;
if (key)
info->control.hw_key = &key->conf;
@ -3446,6 +3457,8 @@ static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
break;
}
}
return TX_CONTINUE;
}
static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
@ -3549,24 +3562,17 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
tx.sta = sta;
tx.key = fast_tx->key;
if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
tx.skb = skb;
r = ieee80211_tx_h_rate_ctrl(&tx);
skb = tx.skb;
tx.skb = NULL;
if (r != TX_CONTINUE) {
if (r != TX_QUEUED)
kfree_skb(skb);
return true;
}
}
if (ieee80211_queue_skb(local, sdata, sta, skb))
return true;
ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
fast_tx->key, skb);
tx.skb = skb;
r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
fast_tx->key, &tx);
tx.skb = NULL;
if (r == TX_DROP) {
kfree_skb(skb);
return true;
}
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
@ -3671,8 +3677,16 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
else
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
r = ieee80211_tx_h_rate_ctrl(&tx);
if (r != TX_CONTINUE) {
ieee80211_free_txskb(&local->hw, skb);
goto begin;
}
}
goto encap_out;
}
if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
struct sta_info *sta = container_of(txq->sta, struct sta_info,
@ -3683,8 +3697,12 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
(tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
pn_offs = ieee80211_hdrlen(hdr->frame_control);
ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
tx.key, skb);
r = ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
tx.key, &tx);
if (r != TX_CONTINUE) {
ieee80211_free_txskb(&local->hw, skb);
goto begin;
}
} else {
if (invoke_tx_handlers_late(&tx))
goto begin;
@ -3764,102 +3782,259 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
{
struct ieee80211_local *local = hw_to_local(hw);
struct airtime_sched_info *air_sched;
u64 now = ktime_get_boottime_ns();
struct ieee80211_txq *ret = NULL;
struct txq_info *txqi = NULL, *head = NULL;
bool found_eligible_txq = false;
struct airtime_info *air_info;
struct txq_info *txqi = NULL;
struct rb_node *node;
bool first = false;
spin_lock_bh(&local->active_txq_lock[ac]);
air_sched = &local->airtime[ac];
spin_lock_bh(&air_sched->lock);
begin:
txqi = list_first_entry_or_null(&local->active_txqs[ac],
struct txq_info,
schedule_order);
if (!txqi)
goto out;
node = air_sched->schedule_pos;
if (txqi == head) {
if (!found_eligible_txq)
goto out;
else
found_eligible_txq = false;
begin:
if (!node) {
node = rb_first_cached(&air_sched->active_txqs);
first = true;
} else {
node = rb_next(node);
}
if (!head)
head = txqi;
if (txqi->txq.sta) {
struct sta_info *sta = container_of(txqi->txq.sta,
struct sta_info, sta);
bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
s64 deficit = sta->airtime[txqi->txq.ac].deficit;
if (aql_check)
found_eligible_txq = true;
if (deficit < 0)
sta->airtime[txqi->txq.ac].deficit +=
sta->airtime_weight;
if (deficit < 0 || !aql_check) {
list_move_tail(&txqi->schedule_order,
&local->active_txqs[txqi->txq.ac]);
goto begin;
}
}
if (txqi->schedule_round == local->schedule_round[ac])
if (!node)
goto out;
list_del_init(&txqi->schedule_order);
txqi->schedule_round = local->schedule_round[ac];
txqi = container_of(node, struct txq_info, schedule_order);
air_info = to_airtime_info(&txqi->txq);
if (air_info->v_t > air_sched->v_t &&
(!first || !airtime_catchup_v_t(air_sched, air_info->v_t, now)))
goto out;
if (!ieee80211_txq_airtime_check(hw, &txqi->txq)) {
first = false;
goto begin;
}
air_sched->schedule_pos = node;
air_sched->last_schedule_activity = now;
ret = &txqi->txq;
out:
spin_unlock_bh(&local->active_txq_lock[ac]);
spin_unlock_bh(&air_sched->lock);
return ret;
}
EXPORT_SYMBOL(ieee80211_next_txq);
void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
static void __ieee80211_insert_txq(struct rb_root_cached *root,
struct txq_info *txqi)
{
struct rb_node **new = &root->rb_root.rb_node;
struct airtime_info *old_air, *new_air;
struct rb_node *parent = NULL;
struct txq_info *__txqi;
bool leftmost = true;
while (*new) {
parent = *new;
__txqi = rb_entry(parent, struct txq_info, schedule_order);
old_air = to_airtime_info(&__txqi->txq);
new_air = to_airtime_info(&txqi->txq);
if (new_air->v_t <= old_air->v_t) {
new = &parent->rb_left;
} else {
new = &parent->rb_right;
leftmost = false;
}
}
rb_link_node(&txqi->schedule_order, parent, new);
rb_insert_color_cached(&txqi->schedule_order, root, leftmost);
}
void ieee80211_resort_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct airtime_info *air_info = to_airtime_info(txq);
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = to_txq_info(txq);
struct airtime_sched_info *air_sched;
air_sched = &local->airtime[txq->ac];
lockdep_assert_held(&air_sched->lock);
if (!RB_EMPTY_NODE(&txqi->schedule_order)) {
struct airtime_info *a_prev = NULL, *a_next = NULL;
struct txq_info *t_prev, *t_next;
struct rb_node *n_prev, *n_next;
/* Erasing a node can cause an expensive rebalancing operation,
* so we check the previous and next nodes first and only remove
* and re-insert if the current node is not already in the
* correct position.
*/
if ((n_prev = rb_prev(&txqi->schedule_order)) != NULL) {
t_prev = container_of(n_prev, struct txq_info,
schedule_order);
a_prev = to_airtime_info(&t_prev->txq);
}
if ((n_next = rb_next(&txqi->schedule_order)) != NULL) {
t_next = container_of(n_next, struct txq_info,
schedule_order);
a_next = to_airtime_info(&t_next->txq);
}
if ((!a_prev || a_prev->v_t <= air_info->v_t) &&
(!a_next || a_next->v_t > air_info->v_t))
return;
if (air_sched->schedule_pos == &txqi->schedule_order)
air_sched->schedule_pos = n_prev;
rb_erase_cached(&txqi->schedule_order,
&air_sched->active_txqs);
RB_CLEAR_NODE(&txqi->schedule_order);
__ieee80211_insert_txq(&air_sched->active_txqs, txqi);
}
}
void ieee80211_update_airtime_weight(struct ieee80211_local *local,
struct airtime_sched_info *air_sched,
u64 now, bool force)
{
struct airtime_info *air_info, *tmp;
u64 weight_sum = 0;
if (unlikely(!now))
now = ktime_get_boottime_ns();
lockdep_assert_held(&air_sched->lock);
if (!force && (air_sched->last_weight_update <
now - AIRTIME_ACTIVE_DURATION))
return;
list_for_each_entry_safe(air_info, tmp,
&air_sched->active_list, list) {
if (airtime_is_active(air_info, now))
weight_sum += air_info->weight;
else
list_del_init(&air_info->list);
}
airtime_weight_sum_set(air_sched, weight_sum);
air_sched->last_weight_update = now;
}
void ieee80211_schedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
__acquires(txq_lock) __releases(txq_lock)
{
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = to_txq_info(txq);
struct airtime_sched_info *air_sched;
u64 now = ktime_get_boottime_ns();
struct airtime_info *air_info;
u8 ac = txq->ac;
bool was_active;
air_sched = &local->airtime[ac];
air_info = to_airtime_info(txq);
spin_lock_bh(&air_sched->lock);
was_active = airtime_is_active(air_info, now);
airtime_set_active(air_sched, air_info, now);
if (!RB_EMPTY_NODE(&txqi->schedule_order))
goto out;
/* If the station has been inactive for a while, catch up its v_t so it
* doesn't get indefinite priority; see comment above the definition of
* AIRTIME_MAX_BEHIND.
*/
if ((!was_active && air_info->v_t < air_sched->v_t) ||
air_info->v_t < air_sched->v_t - AIRTIME_MAX_BEHIND)
air_info->v_t = air_sched->v_t;
ieee80211_update_airtime_weight(local, air_sched, now, !was_active);
__ieee80211_insert_txq(&air_sched->active_txqs, txqi);
out:
spin_unlock_bh(&air_sched->lock);
}
EXPORT_SYMBOL(ieee80211_schedule_txq);
static void __ieee80211_unschedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq,
bool purge)
{
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = to_txq_info(txq);
struct airtime_sched_info *air_sched;
struct airtime_info *air_info;
air_sched = &local->airtime[txq->ac];
air_info = to_airtime_info(&txqi->txq);
lockdep_assert_held(&air_sched->lock);
if (purge) {
list_del_init(&air_info->list);
ieee80211_update_airtime_weight(local, air_sched, 0, true);
}
if (RB_EMPTY_NODE(&txqi->schedule_order))
return;
if (air_sched->schedule_pos == &txqi->schedule_order)
air_sched->schedule_pos = rb_prev(&txqi->schedule_order);
if (!purge)
airtime_set_active(air_sched, air_info,
ktime_get_boottime_ns());
rb_erase_cached(&txqi->schedule_order,
&air_sched->active_txqs);
RB_CLEAR_NODE(&txqi->schedule_order);
}
void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq,
bool force)
bool purge)
__acquires(txq_lock) __releases(txq_lock)
{
struct ieee80211_local *local = hw_to_local(hw);
spin_lock_bh(&local->airtime[txq->ac].lock);
__ieee80211_unschedule_txq(hw, txq, purge);
spin_unlock_bh(&local->airtime[txq->ac].lock);
}
void ieee80211_return_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq, bool force)
{
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = to_txq_info(txq);
spin_lock_bh(&local->active_txq_lock[txq->ac]);
spin_lock_bh(&local->airtime[txq->ac].lock);
if (list_empty(&txqi->schedule_order) &&
(force || !skb_queue_empty(&txqi->frags) ||
txqi->tin.backlog_packets)) {
/* If airtime accounting is active, always enqueue STAs at the
* head of the list to ensure that they only get moved to the
* back by the airtime DRR scheduler once they have a negative
* deficit. A station that already has a negative deficit will
* get immediately moved to the back of the list on the next
* call to ieee80211_next_txq().
*/
if (txqi->txq.sta && local->airtime_flags &&
wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
list_add(&txqi->schedule_order,
&local->active_txqs[txq->ac]);
else
list_add_tail(&txqi->schedule_order,
&local->active_txqs[txq->ac]);
}
if (!RB_EMPTY_NODE(&txqi->schedule_order) && !force &&
!txq_has_queue(txq))
__ieee80211_unschedule_txq(hw, txq, false);
spin_unlock_bh(&local->active_txq_lock[txq->ac]);
spin_unlock_bh(&local->airtime[txq->ac].lock);
}
EXPORT_SYMBOL(__ieee80211_schedule_txq);
EXPORT_SYMBOL(ieee80211_return_txq);
DEFINE_STATIC_KEY_FALSE(aql_disable);
bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct sta_info *sta;
struct airtime_info *air_info = to_airtime_info(txq);
struct ieee80211_local *local = hw_to_local(hw);
if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
@ -3874,15 +4049,12 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
return true;
sta = container_of(txq->sta, struct sta_info, sta);
if (atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
sta->airtime[txq->ac].aql_limit_low)
if (atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_low)
return true;
if (atomic_read(&local->aql_total_pending_airtime) <
local->aql_threshold &&
atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
sta->airtime[txq->ac].aql_limit_high)
atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_high)
return true;
return false;
@ -3892,63 +4064,85 @@ EXPORT_SYMBOL(ieee80211_txq_airtime_check);
bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct txq_info *first_txqi = NULL, *txqi = to_txq_info(txq);
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
struct sta_info *sta;
u8 ac = txq->ac;
struct airtime_sched_info *air_sched;
struct airtime_info *air_info;
struct rb_node *node = NULL;
bool ret = false;
u64 now;
spin_lock_bh(&local->active_txq_lock[ac]);
if (!txqi->txq.sta)
if (!ieee80211_txq_airtime_check(hw, txq))
return false;
air_sched = &local->airtime[txq->ac];
spin_lock_bh(&air_sched->lock);
if (RB_EMPTY_NODE(&txqi->schedule_order))
goto out;
if (list_empty(&txqi->schedule_order))
goto out;
now = ktime_get_boottime_ns();
list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
schedule_order) {
if (iter == txqi)
break;
/* Like in ieee80211_next_txq(), make sure the first station in the
* scheduling order is eligible for transmission to avoid starvation.
*/
node = rb_first_cached(&air_sched->active_txqs);
if (node) {
first_txqi = container_of(node, struct txq_info,
schedule_order);
air_info = to_airtime_info(&first_txqi->txq);
if (!iter->txq.sta) {
list_move_tail(&iter->schedule_order,
&local->active_txqs[ac]);
continue;
}
sta = container_of(iter->txq.sta, struct sta_info, sta);
if (sta->airtime[ac].deficit < 0)
sta->airtime[ac].deficit += sta->airtime_weight;
list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
if (air_sched->v_t < air_info->v_t)
airtime_catchup_v_t(air_sched, air_info->v_t, now);
}
sta = container_of(txqi->txq.sta, struct sta_info, sta);
if (sta->airtime[ac].deficit >= 0)
goto out;
air_info = to_airtime_info(&txqi->txq);
if (air_info->v_t <= air_sched->v_t) {
air_sched->last_schedule_activity = now;
ret = true;
}
sta->airtime[ac].deficit += sta->airtime_weight;
list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
spin_unlock_bh(&local->active_txq_lock[ac]);
return false;
out:
if (!list_empty(&txqi->schedule_order))
list_del_init(&txqi->schedule_order);
spin_unlock_bh(&local->active_txq_lock[ac]);
return true;
spin_unlock_bh(&air_sched->lock);
return ret;
}
EXPORT_SYMBOL(ieee80211_txq_may_transmit);
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
{
struct ieee80211_local *local = hw_to_local(hw);
struct airtime_sched_info *air_sched = &local->airtime[ac];
spin_lock_bh(&local->active_txq_lock[ac]);
local->schedule_round[ac]++;
spin_unlock_bh(&local->active_txq_lock[ac]);
spin_lock_bh(&air_sched->lock);
air_sched->schedule_pos = NULL;
spin_unlock_bh(&air_sched->lock);
}
EXPORT_SYMBOL(ieee80211_txq_schedule_start);
static void
ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct sk_buff *skb)
{
struct rate_control_ref *ref = sdata->local->rate_ctrl;
u16 tid;
if (!ref || !(ref->ops->capa & RATE_CTRL_CAPA_AMPDU_TRIGGER))
return;
if (!sta || !sta->sta.ht_cap.ht_supported ||
!sta->sta.wme || skb_get_queue_mapping(skb) == IEEE80211_AC_VO ||
skb->protocol == sdata->control_port_protocol)
return;
tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
if (likely(sta->ampdu_mlme.tid_tx[tid]))
return;
ieee80211_start_tx_ba_session(&sta->sta, tid, 0);
}
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags,
@ -3979,6 +4173,8 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
skb_get_hash(skb);
}
ieee80211_aggr_check(sdata, sta, skb);
if (sta) {
struct ieee80211_fast_tx *fast_tx;
@ -4242,6 +4438,8 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
memset(info, 0, sizeof(*info));
ieee80211_aggr_check(sdata, sta, skb);
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
if (tid_tx) {

View file

@ -6,7 +6,7 @@
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*
* utilities for mac80211
*/
@ -1693,7 +1693,10 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
WARN_ON(err);
if (WARN_ON(err)) {
kfree_skb(skb);
return;
}
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
@ -1934,13 +1937,26 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata,
*offset = noffset;
}
he_cap = ieee80211_get_he_sta_cap(sband);
if (he_cap) {
he_cap = ieee80211_get_he_iftype_cap(sband,
ieee80211_vif_type_p2p(&sdata->vif));
if (he_cap &&
cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
IEEE80211_CHAN_NO_HE)) {
pos = ieee80211_ie_build_he_cap(pos, he_cap, end);
if (!pos)
goto out_err;
}
if (sband->band == NL80211_BAND_6GHZ) {
if (cfg80211_any_usable_channels(local->hw.wiphy,
BIT(NL80211_BAND_6GHZ),
IEEE80211_CHAN_NO_HE)) {
struct ieee80211_supported_band *sband6;
sband6 = local->hw.wiphy->bands[NL80211_BAND_6GHZ];
he_cap = ieee80211_get_he_iftype_cap(sband6,
ieee80211_vif_type_p2p(&sdata->vif));
if (he_cap) {
enum nl80211_iftype iftype =
ieee80211_vif_type_p2p(&sdata->vif);
__le16 cap = ieee80211_get_he_6ghz_capa(sband, iftype);
@ -2944,12 +2960,15 @@ void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
u8 *pos;
u16 cap;
sband = ieee80211_get_sband(sdata);
if (!sband)
if (!cfg80211_any_usable_channels(sdata->local->hw.wiphy,
BIT(NL80211_BAND_6GHZ),
IEEE80211_CHAN_NO_HE))
return;
sband = sdata->local->hw.wiphy->bands[NL80211_BAND_6GHZ];
iftd = ieee80211_get_sband_iftype_data(sband, iftype);
if (WARN_ON(!iftd))
if (!iftd)
return;
/* Check for device HE 6 GHz capability before adding element */

View file

@ -6,7 +6,7 @@
*
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2018-2020 Intel Corporation
* Copyright 2018-2021 Intel Corporation
*/
#include <linux/export.h>
@ -942,7 +942,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
struct ieee80211_sta_vht_cap *vht_cap;
struct ieee80211_edmg *edmg_cap;
u32 width, control_freq, cap;
bool support_80_80 = false;
bool ext_nss_cap, support_80_80 = false;
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
return false;
@ -950,6 +950,8 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) &
IEEE80211_VHT_EXT_NSS_BW_CAPABLE;
if (edmg_cap->channels &&
!cfg80211_edmg_usable(wiphy,
@ -1015,7 +1017,8 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
(cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) ||
(cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) ||
u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1;
(ext_nss_cap &&
u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1);
if (chandef->chan->band != NL80211_BAND_6GHZ && !support_80_80)
return false;
fallthrough;
@ -1037,7 +1040,8 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ &&
!(vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK))
!(ext_nss_cap &&
(vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)))
return false;
break;
default:
@ -1335,3 +1339,34 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
WARN_ON(1);
}
}
bool cfg80211_any_usable_channels(struct wiphy *wiphy,
unsigned long sband_mask,
u32 prohibited_flags)
{
int idx;
prohibited_flags |= IEEE80211_CHAN_DISABLED;
for_each_set_bit(idx, &sband_mask, NUM_NL80211_BANDS) {
struct ieee80211_supported_band *sband = wiphy->bands[idx];
int chanidx;
if (!sband)
continue;
for (chanidx = 0; chanidx < sband->n_channels; chanidx++) {
struct ieee80211_channel *chan;
chan = &sband->channels[chanidx];
if (chan->flags & prohibited_flags)
continue;
return true;
}
}
return false;
}
EXPORT_SYMBOL(cfg80211_any_usable_channels);

View file

@ -5,7 +5,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -532,11 +532,11 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
wiphy_net_set(&rdev->wiphy, &init_net);
rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block;
rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
&rdev->wiphy.dev, RFKILL_TYPE_WLAN,
&rdev->rfkill_ops, rdev);
rdev->wiphy.rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
&rdev->wiphy.dev, RFKILL_TYPE_WLAN,
&rdev->rfkill_ops, rdev);
if (!rdev->rfkill) {
if (!rdev->wiphy.rfkill) {
wiphy_free(&rdev->wiphy);
return NULL;
}
@ -589,14 +589,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
if (WARN_ON(!c->num_different_channels))
return -EINVAL;
/*
* Put a sane limit on maximum number of different
* channels to simplify channel accounting code.
*/
if (WARN_ON(c->num_different_channels >
CFG80211_MAX_NUM_DIFFERENT_CHANNELS))
return -EINVAL;
/* DFS only works on one channel. */
if (WARN_ON(c->radar_detect_widths &&
(c->num_different_channels > 1)))
@ -936,9 +928,6 @@ int wiphy_register(struct wiphy *wiphy)
return res;
}
/* set up regulatory info */
wiphy_regulatory_register(wiphy);
list_add_rcu(&rdev->list, &cfg80211_rdev_list);
cfg80211_rdev_list_generation++;
@ -949,6 +938,9 @@ int wiphy_register(struct wiphy *wiphy)
cfg80211_debugfs_rdev_add(rdev);
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
/* set up regulatory info */
wiphy_regulatory_register(wiphy);
if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
struct regulatory_request request;
@ -993,10 +985,10 @@ int wiphy_register(struct wiphy *wiphy)
rdev->wiphy.registered = true;
rtnl_unlock();
res = rfkill_register(rdev->rfkill);
res = rfkill_register(rdev->wiphy.rfkill);
if (res) {
rfkill_destroy(rdev->rfkill);
rdev->rfkill = NULL;
rfkill_destroy(rdev->wiphy.rfkill);
rdev->wiphy.rfkill = NULL;
wiphy_unregister(&rdev->wiphy);
return res;
}
@ -1012,18 +1004,10 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy)
if (!rdev->ops->rfkill_poll)
return;
rdev->rfkill_ops.poll = cfg80211_rfkill_poll;
rfkill_resume_polling(rdev->rfkill);
rfkill_resume_polling(wiphy->rfkill);
}
EXPORT_SYMBOL(wiphy_rfkill_start_polling);
void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
rfkill_pause_polling(rdev->rfkill);
}
EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
void wiphy_unregister(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
@ -1035,8 +1019,8 @@ void wiphy_unregister(struct wiphy *wiphy)
wiphy_unlock(&rdev->wiphy);
__count == 0; }));
if (rdev->rfkill)
rfkill_unregister(rdev->rfkill);
if (rdev->wiphy.rfkill)
rfkill_unregister(rdev->wiphy.rfkill);
rtnl_lock();
wiphy_lock(&rdev->wiphy);
@ -1088,7 +1072,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
{
struct cfg80211_internal_bss *scan, *tmp;
struct cfg80211_beacon_registration *reg, *treg;
rfkill_destroy(rdev->rfkill);
rfkill_destroy(rdev->wiphy.rfkill);
list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
list_del(&reg->list);
kfree(reg);
@ -1110,7 +1094,7 @@ void wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked,
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
if (rfkill_set_hw_state_reason(rdev->rfkill, blocked, reason))
if (rfkill_set_hw_state_reason(wiphy->rfkill, blocked, reason))
schedule_work(&rdev->rfkill_block);
}
EXPORT_SYMBOL(wiphy_rfkill_set_hw_state_reason);
@ -1503,7 +1487,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
wdev->use_4addr, 0))
return notifier_from_errno(-EOPNOTSUPP);
if (rfkill_blocked(rdev->rfkill))
if (rfkill_blocked(rdev->wiphy.rfkill))
return notifier_from_errno(-ERFKILL);
break;
default:

View file

@ -3,7 +3,7 @@
* Wireless configuration interface internals.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
@ -27,7 +27,6 @@ struct cfg80211_registered_device {
/* rfkill support */
struct rfkill_ops rfkill_ops;
struct rfkill *rfkill;
struct work_struct rfkill_block;
/* ISO / IEC 3166 alpha2 for which this device is receiving

View file

@ -330,7 +330,7 @@ nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {
};
static const struct nla_policy
nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
[NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,
[NL80211_PMSR_PEER_ATTR_CHAN] = NLA_POLICY_NESTED(nl80211_policy),
[NL80211_PMSR_PEER_ATTR_REQ] =
@ -345,7 +345,7 @@ nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
[NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT },
[NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT },
[NL80211_PMSR_ATTR_PEERS] =
NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
NLA_POLICY_NESTED_ARRAY(nl80211_pmsr_peer_attr_policy),
};
static const struct nla_policy
@ -1731,6 +1731,11 @@ nl80211_send_iftype_data(struct sk_buff *msg,
&iftdata->he_6ghz_capa))
return -ENOBUFS;
if (iftdata->vendor_elems.data && iftdata->vendor_elems.len &&
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
iftdata->vendor_elems.len, iftdata->vendor_elems.data))
return -ENOBUFS;
return 0;
}
@ -4781,11 +4786,10 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
sband->ht_cap.mcs.rx_mask,
sizeof(mask->control[i].ht_mcs));
if (!sband->vht_cap.vht_supported)
continue;
vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
if (sband->vht_cap.vht_supported) {
vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
}
he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype);
if (!he_cap)
@ -13042,7 +13046,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
if (wdev_running(wdev))
return 0;
if (rfkill_blocked(rdev->rfkill))
if (rfkill_blocked(rdev->wiphy.rfkill))
return -ERFKILL;
err = rdev_start_p2p_device(rdev, wdev);
@ -13084,7 +13088,7 @@ static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info)
if (wdev_running(wdev))
return -EEXIST;
if (rfkill_blocked(rdev->rfkill))
if (rfkill_blocked(rdev->wiphy.rfkill))
return -ERFKILL;
if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF])

View file

@ -168,6 +168,18 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
if (tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]) {
if (!out->ftm.non_trigger_based && !out->ftm.trigger_based) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR],
"FTM: BSS color set for EDCA based ranging");
return -EINVAL;
}
out->ftm.bss_color =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]);
}
return 0;
}

View file

@ -464,8 +464,18 @@ static inline int rdev_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_assoc_request *req)
{
const struct cfg80211_bss_ies *bss_ies;
int ret;
trace_rdev_assoc(&rdev->wiphy, dev, req);
/*
* Note: we might trace not exactly the data that's processed,
* due to races and the driver/mac80211 getting a newer copy.
*/
rcu_read_lock();
bss_ies = rcu_dereference(req->bss->ies);
trace_rdev_assoc(&rdev->wiphy, dev, req, bss_ies);
rcu_read_unlock();
ret = rdev->ops->assoc(&rdev->wiphy, dev, req);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;

View file

@ -3975,7 +3975,9 @@ static int __regulatory_set_wiphy_regd(struct wiphy *wiphy,
"wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n"))
return -EPERM;
if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) {
if (WARN(!is_valid_rd(rd),
"Invalid regulatory domain detected: %c%c\n",
rd->alpha2[0], rd->alpha2[1])) {
print_regdomain_info(rd);
return -EINVAL;
}
@ -4049,6 +4051,7 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
wiphy_update_regulatory(wiphy, lr->initiator);
wiphy_all_share_dfs_chan_state(wiphy);
reg_process_self_managed_hints();
}
void wiphy_regulatory_deregister(struct wiphy *wiphy)

View file

@ -5,7 +5,7 @@
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2016 Intel Deutschland GmbH
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2021 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/slab.h>
@ -618,7 +618,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
freq = ieee80211_channel_to_frequency(ap_info->channel, band);
if (end - pos < count * ap_info->tbtt_info_len)
if (end - pos < count * length)
break;
/*
@ -630,7 +630,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
if (band != NL80211_BAND_6GHZ ||
(length != IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM &&
length < IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM)) {
pos += count * ap_info->tbtt_info_len;
pos += count * length;
continue;
}
@ -653,7 +653,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
kfree(entry);
}
pos += ap_info->tbtt_info_len;
pos += length;
}
}
@ -757,7 +757,8 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev)
}
request = kzalloc(struct_size(request, channels, n_channels) +
sizeof(*request->scan_6ghz_params) * count,
sizeof(*request->scan_6ghz_params) * count +
sizeof(*request->ssids) * rdev_req->n_ssids,
GFP_KERNEL);
if (!request) {
cfg80211_free_coloc_ap_list(&coloc_ap_list);
@ -848,9 +849,18 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev)
if (request->n_channels) {
struct cfg80211_scan_request *old = rdev->int_scan_req;
rdev->int_scan_req = request;
/*
* Add the ssids from the parent scan request to the new scan
* request, so the driver would be able to use them in its
* probe requests to discover hidden APs on PSC channels.
*/
request->ssids = (void *)&request->channels[request->n_channels];
request->n_ssids = rdev_req->n_ssids;
memcpy(request->ssids, rdev_req->ssids, sizeof(*request->ssids) *
request->n_ssids);
/*
* If this scan follows a previous scan, save the scan start
* info from the first part of the scan

View file

@ -1195,8 +1195,9 @@ TRACE_EVENT(rdev_auth,
TRACE_EVENT(rdev_assoc,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_assoc_request *req),
TP_ARGS(wiphy, netdev, req),
struct cfg80211_assoc_request *req,
const struct cfg80211_bss_ies *bss_ies),
TP_ARGS(wiphy, netdev, req, bss_ies),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
@ -1204,6 +1205,17 @@ TRACE_EVENT(rdev_assoc,
MAC_ENTRY(prev_bssid)
__field(bool, use_mfp)
__field(u32, flags)
__dynamic_array(u8, bss_elements, bss_ies->len)
__field(bool, bss_elements_bcon)
__field(u64, bss_elements_tsf)
__dynamic_array(u8, elements, req->ie_len)
__array(u8, ht_capa, sizeof(struct ieee80211_ht_cap))
__array(u8, ht_capa_mask, sizeof(struct ieee80211_ht_cap))
__array(u8, vht_capa, sizeof(struct ieee80211_vht_cap))
__array(u8, vht_capa_mask, sizeof(struct ieee80211_vht_cap))
__dynamic_array(u8, fils_kek, req->fils_kek_len)
__dynamic_array(u8, fils_nonces,
req->fils_nonces ? 2 * FILS_NONCE_LEN : 0)
),
TP_fast_assign(
WIPHY_ASSIGN;
@ -1215,6 +1227,26 @@ TRACE_EVENT(rdev_assoc,
MAC_ASSIGN(prev_bssid, req->prev_bssid);
__entry->use_mfp = req->use_mfp;
__entry->flags = req->flags;
if (bss_ies->len)
memcpy(__get_dynamic_array(bss_elements),
bss_ies->data, bss_ies->len);
__entry->bss_elements_bcon = bss_ies->from_beacon;
__entry->bss_elements_tsf = bss_ies->tsf;
if (req->ie)
memcpy(__get_dynamic_array(elements),
req->ie, req->ie_len);
memcpy(__entry->ht_capa, &req->ht_capa, sizeof(req->ht_capa));
memcpy(__entry->ht_capa_mask, &req->ht_capa_mask,
sizeof(req->ht_capa_mask));
memcpy(__entry->vht_capa, &req->vht_capa, sizeof(req->vht_capa));
memcpy(__entry->vht_capa_mask, &req->vht_capa_mask,
sizeof(req->vht_capa_mask));
if (req->fils_kek)
memcpy(__get_dynamic_array(fils_kek),
req->fils_kek, req->fils_kek_len);
if (req->fils_nonces)
memcpy(__get_dynamic_array(fils_nonces),
req->fils_nonces, 2 * FILS_NONCE_LEN);
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u",

View file

@ -902,7 +902,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
/* only change when not disabling */
if (!data->txpower.disabled) {
rfkill_set_sw_state(rdev->rfkill, false);
rfkill_set_sw_state(rdev->wiphy.rfkill, false);
if (data->txpower.fixed) {
/*
@ -927,7 +927,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
}
}
} else {
if (rfkill_set_sw_state(rdev->rfkill, true))
if (rfkill_set_sw_state(rdev->wiphy.rfkill, true))
schedule_work(&rdev->rfkill_block);
return 0;
}
@ -963,7 +963,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
/* well... oh well */
data->txpower.fixed = 1;
data->txpower.disabled = rfkill_blocked(rdev->rfkill);
data->txpower.disabled = rfkill_blocked(rdev->wiphy.rfkill);
data->txpower.value = val;
data->txpower.flags = IW_TXPOW_DBM;
@ -1167,7 +1167,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
bool ps = wdev->ps;
bool ps;
int timeout = wdev->ps_timeout;
int err;

View file

@ -120,8 +120,8 @@ int iw_handler_set_thrspy(struct net_device * dev,
return -EOPNOTSUPP;
/* Just do it */
memcpy(&(spydata->spy_thr_low), &(threshold->low),
2 * sizeof(struct iw_quality));
spydata->spy_thr_low = threshold->low;
spydata->spy_thr_high = threshold->high;
/* Clear flag */
memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
@ -147,8 +147,8 @@ int iw_handler_get_thrspy(struct net_device * dev,
return -EOPNOTSUPP;
/* Just do it */
memcpy(&(threshold->low), &(spydata->spy_thr_low),
2 * sizeof(struct iw_quality));
threshold->low = spydata->spy_thr_low;
threshold->high = spydata->spy_thr_high;
return 0;
}
@ -173,10 +173,10 @@ static void iw_send_thrspy_event(struct net_device * dev,
memcpy(threshold.addr.sa_data, address, ETH_ALEN);
threshold.addr.sa_family = ARPHRD_ETHER;
/* Copy stats */
memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
threshold.qual = *wstats;
/* Copy also thresholds */
memcpy(&(threshold.low), &(spydata->spy_thr_low),
2 * sizeof(struct iw_quality));
threshold.low = spydata->spy_thr_low;
threshold.high = spydata->spy_thr_high;
/* Send event to user space */
wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);