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:
commit
007b312c6f
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -1124,7 +1124,7 @@ enddiscovery:
|
|||
* 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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -150,7 +150,7 @@ out:
|
|||
* 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)
|
||||
|
|
|
@ -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 @@ skip_rates:
|
|||
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 @@ skip_rates:
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1920,7 +1920,7 @@ out:
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 @@ out:
|
|||
* 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 @@ begin:
|
|||
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 @@ begin:
|
|||
(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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ use_default_name:
|
|||
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(®->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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 @@ skip:
|
|||
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue