5558 lines
179 KiB
Diff
5558 lines
179 KiB
Diff
commit ccac50514f793a8a52e9ab6734b6ff0029b1dda4
|
|
Author: Patrick Walther <patrick.walther@netmodule.com>
|
|
Date: Wed Sep 14 14:25:55 2022 +0200
|
|
|
|
backport of subsys patches from openwrt
|
|
|
|
%% original patch: 0002-backport-of-subsys-patches-from-openwrt.patch
|
|
|
|
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
|
|
index bfc2b1f..ea0749c 100644
|
|
--- a/drivers/net/wireless/mac80211_hwsim.c
|
|
+++ b/drivers/net/wireless/mac80211_hwsim.c
|
|
@@ -3004,15 +3004,19 @@ static void mac80211_hwsim_he_capab(struct ieee80211_supported_band *sband)
|
|
{
|
|
u16 n_iftype_data;
|
|
|
|
- if (sband->band == NL80211_BAND_2GHZ) {
|
|
+ switch (sband->band) {
|
|
+ case NL80211_BAND_2GHZ:
|
|
n_iftype_data = ARRAY_SIZE(he_capa_2ghz);
|
|
sband->iftype_data =
|
|
(struct ieee80211_sband_iftype_data *)he_capa_2ghz;
|
|
- } else if (sband->band == NL80211_BAND_5GHZ) {
|
|
+ break;
|
|
+ case NL80211_BAND_5GHZ:
|
|
+ case NL80211_BAND_6GHZ:
|
|
n_iftype_data = ARRAY_SIZE(he_capa_5ghz);
|
|
sband->iftype_data =
|
|
(struct ieee80211_sband_iftype_data *)he_capa_5ghz;
|
|
- } else {
|
|
+ break;
|
|
+ default:
|
|
return;
|
|
}
|
|
|
|
@@ -3302,6 +3306,12 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
|
sband->vht_cap.vht_mcs.tx_mcs_map =
|
|
sband->vht_cap.vht_mcs.rx_mcs_map;
|
|
break;
|
|
+ case NL80211_BAND_6GHZ:
|
|
+ sband->channels = data->channels_6ghz;
|
|
+ sband->n_channels = ARRAY_SIZE(hwsim_channels_6ghz);
|
|
+ sband->bitrates = data->rates + 4;
|
|
+ sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
|
|
+ break;
|
|
case NL80211_BAND_S1GHZ:
|
|
memcpy(&sband->s1g_cap, &hwsim_s1g_cap,
|
|
sizeof(sband->s1g_cap));
|
|
@@ -3312,6 +3322,13 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
|
continue;
|
|
}
|
|
|
|
+ mac80211_hwsim_he_capab(sband);
|
|
+
|
|
+ hw->wiphy->bands[band] = sband;
|
|
+
|
|
+ if (band == NL80211_BAND_6GHZ)
|
|
+ continue;
|
|
+
|
|
sband->ht_cap.ht_supported = true;
|
|
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
|
IEEE80211_HT_CAP_GRN_FLD |
|
|
@@ -3325,10 +3342,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
|
sband->ht_cap.mcs.rx_mask[0] = 0xff;
|
|
sband->ht_cap.mcs.rx_mask[1] = 0xff;
|
|
sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
|
|
-
|
|
- mac80211_hwsim_he_capab(sband);
|
|
-
|
|
- hw->wiphy->bands[band] = sband;
|
|
}
|
|
|
|
/* By default all radios belong to the first group */
|
|
@@ -3747,6 +3760,8 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
|
|
|
rx_status.band = channel->band;
|
|
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
|
|
+ if (rx_status.rate_idx >= data2->hw->wiphy->bands[rx_status.band]->n_bitrates)
|
|
+ goto out;
|
|
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
|
|
|
|
hdr = (void *)skb->data;
|
|
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
|
|
index 30c9ebd..ab83553 100644
|
|
--- a/include/net/cfg80211.h
|
|
+++ b/include/net/cfg80211.h
|
|
@@ -1045,6 +1045,36 @@ struct cfg80211_crypto_settings {
|
|
enum nl80211_sae_pwe_mechanism sae_pwe;
|
|
};
|
|
|
|
+/**
|
|
+ * struct cfg80211_mbssid_config - AP settings for multi bssid
|
|
+ *
|
|
+ * @tx_wdev: pointer to the transmitted interface in the MBSSID set
|
|
+ * @index: index of this AP in the multi bssid group.
|
|
+ * @ema: set to true if the beacons should be sent out in EMA mode.
|
|
+ */
|
|
+struct cfg80211_mbssid_config {
|
|
+ struct wireless_dev *tx_wdev;
|
|
+ u8 index;
|
|
+ bool ema;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct cfg80211_mbssid_elems - Multiple BSSID elements
|
|
+ *
|
|
+ * @cnt: Number of elements in array %elems.
|
|
+ *
|
|
+ * @elem: Array of multiple BSSID element(s) to be added into Beacon frames.
|
|
+ * @elem.data: Data for multiple BSSID elements.
|
|
+ * @elem.len: Length of data.
|
|
+ */
|
|
+struct cfg80211_mbssid_elems {
|
|
+ u8 cnt;
|
|
+ struct {
|
|
+ const u8 *data;
|
|
+ size_t len;
|
|
+ } elem[];
|
|
+};
|
|
+
|
|
/**
|
|
* struct cfg80211_beacon_data - beacon data
|
|
* @head: head portion of beacon (before TIM IE)
|
|
@@ -1063,6 +1093,7 @@ struct cfg80211_crypto_settings {
|
|
* @assocresp_ies_len: length of assocresp_ies in octets
|
|
* @probe_resp_len: length of probe response template (@probe_resp)
|
|
* @probe_resp: probe response template (AP mode only)
|
|
+ * @mbssid_ies: multiple BSSID elements
|
|
* @ftm_responder: enable FTM responder functionality; -1 for no change
|
|
* (which also implies no change in LCI/civic location data)
|
|
* @lci: Measurement Report element content, starting with Measurement Token
|
|
@@ -1080,6 +1111,7 @@ struct cfg80211_beacon_data {
|
|
const u8 *probe_resp;
|
|
const u8 *lci;
|
|
const u8 *civicloc;
|
|
+ struct cfg80211_mbssid_elems *mbssid_ies;
|
|
s8 ftm_responder;
|
|
|
|
size_t head_len, tail_len;
|
|
@@ -1194,6 +1226,7 @@ enum cfg80211_ap_settings_flags {
|
|
* @he_oper: HE operation IE (or %NULL if HE isn't enabled)
|
|
* @fils_discovery: FILS discovery transmission parameters
|
|
* @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
|
|
+ * @mbssid_config: AP settings for multiple bssid
|
|
*/
|
|
struct cfg80211_ap_settings {
|
|
struct cfg80211_chan_def chandef;
|
|
@@ -1226,6 +1259,7 @@ struct cfg80211_ap_settings {
|
|
struct cfg80211_he_bss_color he_bss_color;
|
|
struct cfg80211_fils_discovery fils_discovery;
|
|
struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
|
|
+ struct cfg80211_mbssid_config mbssid_config;
|
|
};
|
|
|
|
/**
|
|
@@ -3835,6 +3869,7 @@ struct mgmt_frame_regs {
|
|
* (as advertised by the nl80211 feature flag.)
|
|
* @get_tx_power: store the current TX power into the dbm variable;
|
|
* return 0 if successful
|
|
+ * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary
|
|
*
|
|
* @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
|
|
* functions to adjust rfkill hw state
|
|
@@ -4023,6 +4058,15 @@ struct mgmt_frame_regs {
|
|
* @set_sar_specs: Update the SAR (TX power) settings.
|
|
*
|
|
* @color_change: Initiate a color change.
|
|
+ *
|
|
+ * @set_radar_background: Configure dedicated offchannel chain available for
|
|
+ * radar/CAC detection on some hw. This chain can't be used to transmit
|
|
+ * or receive frames and it is bounded to a running wdev.
|
|
+ * Background radar/CAC detection allows to avoid the CAC downtime
|
|
+ * switching to a different channel during CAC detection on the selected
|
|
+ * radar channel.
|
|
+ * The caller is expected to set chandef pointer to NULL in order to
|
|
+ * disable background CAC/radar detection.
|
|
*/
|
|
struct cfg80211_ops {
|
|
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
|
@@ -4159,6 +4203,7 @@ struct cfg80211_ops {
|
|
enum nl80211_tx_power_setting type, int mbm);
|
|
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
int *dbm);
|
|
+ int (*set_antenna_gain)(struct wiphy *wiphy, int dbi);
|
|
|
|
void (*rfkill_poll)(struct wiphy *wiphy);
|
|
|
|
@@ -4353,6 +4398,8 @@ struct cfg80211_ops {
|
|
int (*color_change)(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
struct cfg80211_color_change_settings *params);
|
|
+ int (*set_radar_background)(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef);
|
|
};
|
|
|
|
/*
|
|
@@ -4986,6 +5033,13 @@ struct wiphy_iftype_akm_suites {
|
|
* %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
|
|
* @sar_capa: SAR control capabilities
|
|
* @rfkill: a pointer to the rfkill structure
|
|
+ *
|
|
+ * @mbssid_max_interfaces: maximum number of interfaces supported by the driver
|
|
+ * in a multiple BSSID set. This field must be set to a non-zero value
|
|
+ * by the driver to advertise MBSSID support.
|
|
+ * @mbssid_max_ema_profile_periodicity: maximum profile periodicity supported by
|
|
+ * the driver. Setting this field to a non-zero value indicates that the
|
|
+ * driver supports enhanced multi-BSSID advertisements (EMA AP).
|
|
*/
|
|
struct wiphy {
|
|
struct mutex mtx;
|
|
@@ -5133,6 +5187,9 @@ struct wiphy {
|
|
|
|
struct rfkill *rfkill;
|
|
|
|
+ u8 mbssid_max_interfaces;
|
|
+ u8 ema_max_profile_periodicity;
|
|
+
|
|
char priv[] __aligned(NETDEV_ALIGN);
|
|
};
|
|
|
|
@@ -7525,15 +7582,33 @@ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
|
|
void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
|
|
|
|
/**
|
|
- * cfg80211_radar_event - radar detection event
|
|
+ * __cfg80211_radar_event - radar detection event
|
|
* @wiphy: the wiphy
|
|
* @chandef: chandef for the current channel
|
|
+ * @offchan: the radar has been detected on the offchannel chain
|
|
* @gfp: context flags
|
|
*
|
|
* This function is called when a radar is detected on the current chanenl.
|
|
*/
|
|
-void cfg80211_radar_event(struct wiphy *wiphy,
|
|
- struct cfg80211_chan_def *chandef, gfp_t gfp);
|
|
+void __cfg80211_radar_event(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef,
|
|
+ bool offchan, gfp_t gfp);
|
|
+
|
|
+static inline void
|
|
+cfg80211_radar_event(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef,
|
|
+ gfp_t gfp)
|
|
+{
|
|
+ __cfg80211_radar_event(wiphy, chandef, false, gfp);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+cfg80211_background_radar_event(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef,
|
|
+ gfp_t gfp)
|
|
+{
|
|
+ __cfg80211_radar_event(wiphy, chandef, true, gfp);
|
|
+}
|
|
|
|
/**
|
|
* cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event
|
|
@@ -7564,6 +7639,14 @@ void cfg80211_cac_event(struct net_device *netdev,
|
|
const struct cfg80211_chan_def *chandef,
|
|
enum nl80211_radar_event event, gfp_t gfp);
|
|
|
|
+/**
|
|
+ * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
|
|
+ * @wiphy: the wiphy
|
|
+ *
|
|
+ * This function is called by the driver when a Channel Availability Check
|
|
+ * (CAC) is aborted by a offchannel dedicated chain.
|
|
+ */
|
|
+void cfg80211_background_cac_abort(struct wiphy *wiphy);
|
|
|
|
/**
|
|
* cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
|
|
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
|
|
index a331431..4c2760d 100644
|
|
--- a/include/net/mac80211.h
|
|
+++ b/include/net/mac80211.h
|
|
@@ -1566,6 +1566,7 @@ enum ieee80211_smps_mode {
|
|
*
|
|
* @power_level: requested transmit power (in dBm), backward compatibility
|
|
* value only that is set to the minimum of all interfaces
|
|
+ * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi)
|
|
*
|
|
* @chandef: the channel definition to tune to
|
|
* @radar_enabled: whether radar detection is enabled
|
|
@@ -1586,6 +1587,7 @@ enum ieee80211_smps_mode {
|
|
struct ieee80211_conf {
|
|
u32 flags;
|
|
int power_level, dynamic_ps_timeout;
|
|
+ int max_antenna_gain;
|
|
|
|
u16 listen_interval;
|
|
u8 ps_dtim_period;
|
|
@@ -1719,6 +1721,7 @@ enum ieee80211_offload_flags {
|
|
* write-protected by sdata_lock and local->mtx so holding either is fine
|
|
* for read access.
|
|
* @color_change_color: the bss color that will be used after the change.
|
|
+ * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled.
|
|
*/
|
|
struct ieee80211_vif {
|
|
enum nl80211_iftype type;
|
|
@@ -1750,6 +1753,8 @@ struct ieee80211_vif {
|
|
bool color_change_active;
|
|
u8 color_change_color;
|
|
|
|
+ struct ieee80211_vif *mbssid_tx_vif;
|
|
+
|
|
/* must be last */
|
|
u8 drv_priv[] __aligned(sizeof(void *));
|
|
};
|
|
@@ -2415,6 +2420,9 @@ struct ieee80211_txq {
|
|
* usage and 802.11 frames with %RX_FLAG_ONLY_MONITOR set for monitor to
|
|
* the stack.
|
|
*
|
|
+ * @IEEE80211_HW_DETECTS_COLOR_COLLISION: HW/driver has support for BSS color
|
|
+ * collision detection and doesn't need it in software.
|
|
+ *
|
|
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
|
*/
|
|
enum ieee80211_hw_flags {
|
|
@@ -2470,6 +2478,7 @@ enum ieee80211_hw_flags {
|
|
IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
|
|
IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
|
|
IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
|
|
+ IEEE80211_HW_DETECTS_COLOR_COLLISION,
|
|
|
|
/* keep last, obviously */
|
|
NUM_IEEE80211_HW_FLAGS
|
|
@@ -3937,6 +3946,16 @@ struct ieee80211_prep_tx_info {
|
|
* twt structure.
|
|
* @twt_teardown_request: Update the hw with TWT teardown request received
|
|
* from the peer.
|
|
+ * @set_radar_background: Configure dedicated offchannel chain available for
|
|
+ * radar/CAC detection on some hw. This chain can't be used to transmit
|
|
+ * or receive frames and it is bounded to a running wdev.
|
|
+ * Background radar/CAC detection allows to avoid the CAC downtime
|
|
+ * switching to a different channel during CAC detection on the selected
|
|
+ * radar channel.
|
|
+ * The caller is expected to set chandef pointer to NULL in order to
|
|
+ * disable background CAC/radar detection.
|
|
+ * @net_fill_forward_path: Called from .ndo_fill_forward_path in order to
|
|
+ * resolve a path for hardware flow offloading
|
|
*/
|
|
struct ieee80211_ops {
|
|
void (*tx)(struct ieee80211_hw *hw,
|
|
@@ -4265,6 +4284,15 @@ struct ieee80211_ops {
|
|
struct ieee80211_twt_setup *twt);
|
|
void (*twt_teardown_request)(struct ieee80211_hw *hw,
|
|
struct ieee80211_sta *sta, u8 flowid);
|
|
+ int (*set_radar_background)(struct ieee80211_hw *hw,
|
|
+ struct cfg80211_chan_def *chandef);
|
|
+#if LINUX_VERSION_IS_GEQ(5,13,0)
|
|
+ int (*net_fill_forward_path)(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_vif *vif,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct net_device_path_ctx *ctx,
|
|
+ struct net_device_path *path);
|
|
+#endif
|
|
};
|
|
|
|
/**
|
|
@@ -4916,12 +4944,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
|
|
* @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets
|
|
* to countdown counters. This array can contain zero values which
|
|
* should be ignored.
|
|
+ * @mbssid_off: position of the multiple bssid element
|
|
*/
|
|
struct ieee80211_mutable_offsets {
|
|
u16 tim_offset;
|
|
u16 tim_length;
|
|
|
|
u16 cntdwn_counter_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
|
|
+ u16 mbssid_off;
|
|
};
|
|
|
|
/**
|
|
@@ -6642,6 +6672,9 @@ 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
|
|
*
|
|
@@ -6654,7 +6687,11 @@ static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
|
* The driver may call this function if it has buffered packets for
|
|
* this TXQ internally.
|
|
*/
|
|
-void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
|
+static inline void
|
|
+ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
|
+{
|
|
+ __ieee80211_schedule_txq(hw, txq, true);
|
|
+}
|
|
|
|
/**
|
|
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
|
@@ -6666,8 +6703,12 @@ void 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.
|
|
*/
|
|
-void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
|
|
- bool force);
|
|
+static inline void
|
|
+ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
|
|
+ bool force)
|
|
+{
|
|
+ __ieee80211_schedule_txq(hw, txq, force);
|
|
+}
|
|
|
|
/**
|
|
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
|
|
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
|
|
index c2efea9..019f065 100644
|
|
--- a/include/uapi/linux/nl80211.h
|
|
+++ b/include/uapi/linux/nl80211.h
|
|
@@ -337,7 +337,10 @@
|
|
* @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
|
|
* %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
|
|
* userspace to request deletion of a virtual interface, then requires
|
|
- * attribute %NL80211_ATTR_IFINDEX.
|
|
+ * attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are
|
|
+ * enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS,
|
|
+ * and if this command is used for the transmitting interface, then all
|
|
+ * the non-transmitting interfaces are deleted as well.
|
|
*
|
|
* @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
|
|
* by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
|
|
@@ -2593,6 +2596,28 @@ enum nl80211_commands {
|
|
* @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
|
|
* information for the time while performing a color switch.
|
|
*
|
|
+ * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID
|
|
+ * advertisements (MBSSID) parameters in AP mode.
|
|
+ * Kernel uses this attribute to indicate the driver's support for MBSSID
|
|
+ * and enhanced multi-BSSID advertisements (EMA AP) to the userspace.
|
|
+ * Userspace should use this attribute to configure per interface MBSSID
|
|
+ * parameters.
|
|
+ * See &enum nl80211_mbssid_config_attributes for details.
|
|
+ *
|
|
+ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements.
|
|
+ * Mandatory parameter for the transmitting interface to enable MBSSID.
|
|
+ * Optional for the non-transmitting interfaces.
|
|
+ *
|
|
+ * @NL80211_ATTR_RADAR_BACKGROUND: Configure dedicated offchannel chain
|
|
+ * available for radar/CAC detection on some hw. This chain can't be used
|
|
+ * to transmit or receive frames and it is bounded to a running wdev.
|
|
+ * Background radar/CAC detection allows to avoid the CAC downtime
|
|
+ * switching on a different channel during CAC detection on the selected
|
|
+ * radar channel.
|
|
+ *
|
|
+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
|
|
+ * transmit power to stay within regulatory limits. u32, dBi.
|
|
+ *
|
|
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
|
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
|
* @__NL80211_ATTR_AFTER_LAST: internal use
|
|
@@ -3096,6 +3121,13 @@ enum nl80211_attrs {
|
|
NL80211_ATTR_COLOR_CHANGE_COLOR,
|
|
NL80211_ATTR_COLOR_CHANGE_ELEMS,
|
|
|
|
+ NL80211_ATTR_MBSSID_CONFIG,
|
|
+ NL80211_ATTR_MBSSID_ELEMS,
|
|
+
|
|
+ NL80211_ATTR_RADAR_BACKGROUND,
|
|
+
|
|
+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
|
|
+
|
|
/* add attributes here, update the policy in nl80211.c */
|
|
|
|
__NL80211_ATTR_AFTER_LAST,
|
|
@@ -5995,6 +6027,14 @@ enum nl80211_feature_flags {
|
|
* @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
|
|
* detection and change announcemnts.
|
|
*
|
|
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports
|
|
+ * FILS encryption and decryption for (Re)Association Request and Response
|
|
+ * frames. Userspace has to share FILS AAD details to the driver by using
|
|
+ * @NL80211_CMD_SET_FILS_AAD.
|
|
+ *
|
|
+ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
|
|
+ * detection.
|
|
+ *
|
|
* @NUM_NL80211_EXT_FEATURES: number of extended features.
|
|
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
|
|
*/
|
|
@@ -6060,6 +6100,8 @@ enum nl80211_ext_feature_index {
|
|
NL80211_EXT_FEATURE_SECURE_RTT,
|
|
NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
|
|
NL80211_EXT_FEATURE_BSS_COLOR,
|
|
+ NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
|
|
+ NL80211_EXT_FEATURE_RADAR_BACKGROUND,
|
|
|
|
/* add new features before the definition below */
|
|
NUM_NL80211_EXT_FEATURES,
|
|
@@ -7349,4 +7391,60 @@ enum nl80211_sar_specs_attrs {
|
|
NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
|
|
};
|
|
|
|
+/**
|
|
+ * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced
|
|
+ * multi-BSSID advertisements (EMA) in AP mode.
|
|
+ * Kernel uses some of these attributes to advertise driver's support for
|
|
+ * MBSSID and EMA.
|
|
+ * Remaining attributes should be used by the userspace to configure the
|
|
+ * features.
|
|
+ *
|
|
+ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
|
|
+ *
|
|
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise
|
|
+ * the maximum number of MBSSID interfaces supported by the driver.
|
|
+ * Driver should indicate MBSSID support by setting
|
|
+ * wiphy->mbssid_max_interfaces to a value more than or equal to 2.
|
|
+ *
|
|
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
|
|
+ * to advertise the maximum profile periodicity supported by the driver
|
|
+ * if EMA is enabled. Driver should indicate EMA support to the userspace
|
|
+ * by setting wiphy->mbssid_max_ema_profile_periodicity to
|
|
+ * a non-zero value.
|
|
+ *
|
|
+ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
|
|
+ * this BSS (u8) in the multiple BSSID set.
|
|
+ * Value must be set to 0 for the transmitting interface and non-zero for
|
|
+ * all non-transmitting interfaces. The userspace will be responsible
|
|
+ * for using unique indices for the interfaces.
|
|
+ * Range: 0 to wiphy->mbssid_max_interfaces-1.
|
|
+ *
|
|
+ * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for
|
|
+ * a non-transmitted profile which provides the interface index (u32) of
|
|
+ * the transmitted profile. The value must match one of the interface
|
|
+ * indices advertised by the kernel. Optional if the interface being set up
|
|
+ * is the transmitting one, however, if provided then the value must match
|
|
+ * the interface index of the same.
|
|
+ *
|
|
+ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
|
|
+ * Setting this flag is permitted only if the driver advertises EMA support
|
|
+ * by setting wiphy->mbssid_max_ema_profile_periodicity to non-zero.
|
|
+ *
|
|
+ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
|
|
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
|
|
+ */
|
|
+enum nl80211_mbssid_config_attributes {
|
|
+ __NL80211_MBSSID_CONFIG_ATTR_INVALID,
|
|
+
|
|
+ NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
|
|
+ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
|
|
+ NL80211_MBSSID_CONFIG_ATTR_INDEX,
|
|
+ NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
|
|
+ NL80211_MBSSID_CONFIG_ATTR_EMA,
|
|
+
|
|
+ /* keep last */
|
|
+ __NL80211_MBSSID_CONFIG_ATTR_LAST,
|
|
+ NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
|
|
+};
|
|
+
|
|
#endif /* __LINUX_NL80211_H */
|
|
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
|
|
index ef729b1..7d2925b 100644
|
|
--- a/net/mac80211/agg-rx.c
|
|
+++ b/net/mac80211/agg-rx.c
|
|
@@ -478,7 +478,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
|
size_t len)
|
|
{
|
|
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
|
|
- struct ieee802_11_elems elems = { };
|
|
+ struct ieee802_11_elems *elems = NULL;
|
|
u8 dialog_token;
|
|
int ies_len;
|
|
|
|
@@ -496,16 +496,18 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
|
ies_len = len - offsetof(struct ieee80211_mgmt,
|
|
u.action.u.addba_req.variable);
|
|
if (ies_len) {
|
|
- ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
|
|
- ies_len, true, &elems, mgmt->bssid, NULL);
|
|
- if (elems.parse_error)
|
|
- return;
|
|
+ elems = ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
|
|
+ ies_len, true, mgmt->bssid, NULL);
|
|
+ if (!elems || elems->parse_error)
|
|
+ goto free;
|
|
}
|
|
|
|
__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
|
|
start_seq_num, ba_policy, tid,
|
|
buf_size, true, false,
|
|
- elems.addba_ext_ie);
|
|
+ elems ? elems->addba_ext_ie : NULL);
|
|
+free:
|
|
+ kfree(elems);
|
|
}
|
|
|
|
void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
|
|
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
|
|
index 1de989c..abe7318 100644
|
|
--- a/net/mac80211/cfg.c
|
|
+++ b/net/mac80211/cfg.c
|
|
@@ -112,6 +112,36 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
|
|
return 0;
|
|
}
|
|
|
|
+static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
|
|
+ struct cfg80211_mbssid_config params)
|
|
+{
|
|
+ struct ieee80211_sub_if_data *tx_sdata;
|
|
+
|
|
+ sdata->vif.mbssid_tx_vif = NULL;
|
|
+ sdata->vif.bss_conf.bssid_index = 0;
|
|
+ sdata->vif.bss_conf.nontransmitted = false;
|
|
+ sdata->vif.bss_conf.ema_ap = false;
|
|
+
|
|
+ if (sdata->vif.type != NL80211_IFTYPE_AP || !params.tx_wdev)
|
|
+ return -EINVAL;
|
|
+
|
|
+ tx_sdata = IEEE80211_WDEV_TO_SUB_IF(params.tx_wdev);
|
|
+ if (!tx_sdata)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (tx_sdata == sdata) {
|
|
+ sdata->vif.mbssid_tx_vif = &sdata->vif;
|
|
+ } else {
|
|
+ sdata->vif.mbssid_tx_vif = &tx_sdata->vif;
|
|
+ sdata->vif.bss_conf.nontransmitted = true;
|
|
+ sdata->vif.bss_conf.bssid_index = params.index;
|
|
+ }
|
|
+ if (params.ema)
|
|
+ sdata->vif.bss_conf.ema_ap = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
|
const char *name,
|
|
unsigned char name_assign_type,
|
|
@@ -959,11 +989,29 @@ static int ieee80211_set_ftm_responder_params(
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+ieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst,
|
|
+ struct cfg80211_mbssid_elems *src)
|
|
+{
|
|
+ int i, offset = 0;
|
|
+
|
|
+ for (i = 0; i < src->cnt; i++) {
|
|
+ memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
|
|
+ dst->elem[i].len = src->elem[i].len;
|
|
+ dst->elem[i].data = pos + offset;
|
|
+ offset += dst->elem[i].len;
|
|
+ }
|
|
+ dst->cnt = src->cnt;
|
|
+
|
|
+ return offset;
|
|
+}
|
|
+
|
|
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|
struct cfg80211_beacon_data *params,
|
|
const struct ieee80211_csa_settings *csa,
|
|
const struct ieee80211_color_change_settings *cca)
|
|
{
|
|
+ struct cfg80211_mbssid_elems *mbssid = NULL;
|
|
struct beacon_data *new, *old;
|
|
int new_head_len, new_tail_len;
|
|
int size, err;
|
|
@@ -991,6 +1039,17 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
size = sizeof(*new) + new_head_len + new_tail_len;
|
|
|
|
+ /* new or old multiple BSSID elements? */
|
|
+ if (params->mbssid_ies) {
|
|
+ mbssid = params->mbssid_ies;
|
|
+ size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
|
|
+ size += ieee80211_get_mbssid_beacon_len(mbssid);
|
|
+ } else if (old && old->mbssid_ies) {
|
|
+ mbssid = old->mbssid_ies;
|
|
+ size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
|
|
+ size += ieee80211_get_mbssid_beacon_len(mbssid);
|
|
+ }
|
|
+
|
|
new = kzalloc(size, GFP_KERNEL);
|
|
if (!new)
|
|
return -ENOMEM;
|
|
@@ -999,12 +1058,23 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
/*
|
|
* pointers go into the block we allocated,
|
|
- * memory is | beacon_data | head | tail |
|
|
+ * memory is | beacon_data | head | tail | mbssid_ies
|
|
*/
|
|
new->head = ((u8 *) new) + sizeof(*new);
|
|
new->tail = new->head + new_head_len;
|
|
new->head_len = new_head_len;
|
|
new->tail_len = new_tail_len;
|
|
+ /* copy in optional mbssid_ies */
|
|
+ if (mbssid) {
|
|
+ u8 *pos = new->tail + new->tail_len;
|
|
+
|
|
+ new->mbssid_ies = (void *)pos;
|
|
+ pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
|
|
+ ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies, mbssid);
|
|
+ /* update bssid_indicator */
|
|
+ sdata->vif.bss_conf.bssid_indicator =
|
|
+ ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
|
|
+ }
|
|
|
|
if (csa) {
|
|
new->cntdwn_current_counter = csa->count;
|
|
@@ -1107,6 +1177,14 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|
changed |= BSS_CHANGED_HE_BSS_COLOR;
|
|
}
|
|
|
|
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
|
|
+ params->mbssid_config.tx_wdev) {
|
|
+ err = ieee80211_set_ap_mbssid_options(sdata,
|
|
+ params->mbssid_config);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
mutex_lock(&local->mtx);
|
|
err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
|
|
IEEE80211_CHANCTX_SHARED);
|
|
@@ -1294,8 +1372,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
- kfree(sdata->u.ap.next_beacon);
|
|
- sdata->u.ap.next_beacon = NULL;
|
|
+ if (sdata->u.ap.next_beacon) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
+ kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
+ }
|
|
|
|
/* turn off carrier for this interface and dependent VLANs */
|
|
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
|
|
@@ -1319,7 +1400,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|
sdata->vif.bss_conf.ftmr_params = NULL;
|
|
|
|
__sta_info_flush(sdata, true);
|
|
- ieee80211_free_keys(sdata, true);
|
|
|
|
sdata->vif.bss_conf.enable_beacon = false;
|
|
sdata->beacon_rate_set = false;
|
|
@@ -1474,38 +1554,6 @@ 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)
|
|
@@ -1693,8 +1741,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|
sta_apply_mesh_params(local, sta, params);
|
|
|
|
if (params->airtime_weight)
|
|
- sta_apply_airtime_params(local, sta, params);
|
|
-
|
|
+ sta->airtime_weight = params->airtime_weight;
|
|
|
|
/* set the STA state after all sta info from usermode has been set */
|
|
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
|
|
@@ -2498,7 +2545,7 @@ static int ieee80211_scan(struct wiphy *wiphy,
|
|
* the frames sent while scanning on other channel will be
|
|
* lost)
|
|
*/
|
|
- if (sdata->u.ap.beacon &&
|
|
+ if (0 && sdata->u.ap.beacon &&
|
|
(!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
|
|
!(req->flags & NL80211_SCAN_FLAG_AP)))
|
|
return -EOPNOTSUPP;
|
|
@@ -2765,6 +2812,19 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy,
|
|
return 0;
|
|
}
|
|
|
|
+static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi)
|
|
+{
|
|
+ struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
+
|
|
+ if (dbi < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ local->user_antenna_gain = dbi;
|
|
+ ieee80211_hw_config(local, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void ieee80211_rfkill_poll(struct wiphy *wiphy)
|
|
{
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
@@ -3089,12 +3149,24 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
|
|
|
|
len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
|
|
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
|
|
- beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
|
|
+ beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len +
|
|
+ ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies);
|
|
|
|
new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
|
|
if (!new_beacon)
|
|
return NULL;
|
|
|
|
+ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
|
|
+ new_beacon->mbssid_ies =
|
|
+ kzalloc(struct_size(new_beacon->mbssid_ies,
|
|
+ elem, beacon->mbssid_ies->cnt),
|
|
+ GFP_KERNEL);
|
|
+ if (!new_beacon->mbssid_ies) {
|
|
+ kfree(new_beacon);
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
pos = (u8 *)(new_beacon + 1);
|
|
if (beacon->head_len) {
|
|
new_beacon->head_len = beacon->head_len;
|
|
@@ -3132,6 +3204,10 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
|
|
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
|
|
pos += beacon->probe_resp_len;
|
|
}
|
|
+ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt)
|
|
+ pos += ieee80211_copy_mbssid_beacon(pos,
|
|
+ new_beacon->mbssid_ies,
|
|
+ beacon->mbssid_ies);
|
|
|
|
/* might copy -1, meaning no changes requested */
|
|
new_beacon->ftm_responder = beacon->ftm_responder;
|
|
@@ -3154,9 +3230,31 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
|
|
void ieee80211_csa_finish(struct ieee80211_vif *vif)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
|
- ieee80211_queue_work(&sdata->local->hw,
|
|
- &sdata->csa_finalize_work);
|
|
+ rcu_read_lock();
|
|
+
|
|
+ if (vif->mbssid_tx_vif == vif) {
|
|
+ /* Trigger ieee80211_csa_finish() on the non-transmitting
|
|
+ * interfaces when channel switch is received on
|
|
+ * transmitting interface
|
|
+ */
|
|
+ struct ieee80211_sub_if_data *iter;
|
|
+
|
|
+ list_for_each_entry_rcu(iter, &local->interfaces, list) {
|
|
+ if (!ieee80211_sdata_running(iter))
|
|
+ continue;
|
|
+
|
|
+ if (iter == sdata || iter->vif.mbssid_tx_vif != vif)
|
|
+ continue;
|
|
+
|
|
+ ieee80211_queue_work(&iter->local->hw,
|
|
+ &iter->csa_finalize_work);
|
|
+ }
|
|
+ }
|
|
+ ieee80211_queue_work(&local->hw, &sdata->csa_finalize_work);
|
|
+
|
|
+ rcu_read_unlock();
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_csa_finish);
|
|
|
|
@@ -3169,8 +3267,11 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|
case NL80211_IFTYPE_AP:
|
|
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
|
|
NULL, NULL);
|
|
- kfree(sdata->u.ap.next_beacon);
|
|
- sdata->u.ap.next_beacon = NULL;
|
|
+ if (sdata->u.ap.next_beacon) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
+ kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
+ }
|
|
|
|
if (err < 0)
|
|
return err;
|
|
@@ -3325,8 +3426,12 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|
if ((params->n_counter_offsets_beacon >
|
|
IEEE80211_MAX_CNTDWN_COUNTERS_NUM) ||
|
|
(params->n_counter_offsets_presp >
|
|
- IEEE80211_MAX_CNTDWN_COUNTERS_NUM))
|
|
+ IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
+ kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
return -EINVAL;
|
|
+ }
|
|
|
|
csa.counter_offsets_beacon = params->counter_offsets_beacon;
|
|
csa.counter_offsets_presp = params->counter_offsets_presp;
|
|
@@ -3336,7 +3441,9 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL);
|
|
if (err < 0) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
return err;
|
|
}
|
|
*changed |= err;
|
|
@@ -3426,8 +3533,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|
static void ieee80211_color_change_abort(struct ieee80211_sub_if_data *sdata)
|
|
{
|
|
sdata->vif.color_change_active = false;
|
|
- kfree(sdata->u.ap.next_beacon);
|
|
- sdata->u.ap.next_beacon = NULL;
|
|
+ if (sdata->u.ap.next_beacon) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
+ kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
+ }
|
|
|
|
cfg80211_color_change_aborted_notify(sdata->dev);
|
|
}
|
|
@@ -4165,8 +4275,11 @@ ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
|
|
NULL, NULL);
|
|
- kfree(sdata->u.ap.next_beacon);
|
|
- sdata->u.ap.next_beacon = NULL;
|
|
+ if (sdata->u.ap.next_beacon) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
+ kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
+ }
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
@@ -4209,7 +4322,11 @@ ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata,
|
|
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change,
|
|
NULL, &color_change);
|
|
if (err < 0) {
|
|
- kfree(sdata->u.ap.next_beacon);
|
|
+ if (sdata->u.ap.next_beacon) {
|
|
+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
|
|
+ kfree(sdata->u.ap.next_beacon);
|
|
+ sdata->u.ap.next_beacon = NULL;
|
|
+ }
|
|
return err;
|
|
}
|
|
*changed |= err;
|
|
@@ -4345,6 +4462,18 @@ out:
|
|
return err;
|
|
}
|
|
|
|
+static int
|
|
+ieee80211_set_radar_background(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
+
|
|
+ if (!local->ops->set_radar_background)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return local->ops->set_radar_background(&local->hw, chandef);
|
|
+}
|
|
+
|
|
const struct cfg80211_ops mac80211_config_ops = {
|
|
.add_virtual_intf = ieee80211_add_iface,
|
|
.del_virtual_intf = ieee80211_del_iface,
|
|
@@ -4400,6 +4529,7 @@ const struct cfg80211_ops mac80211_config_ops = {
|
|
.set_wiphy_params = ieee80211_set_wiphy_params,
|
|
.set_tx_power = ieee80211_set_tx_power,
|
|
.get_tx_power = ieee80211_get_tx_power,
|
|
+ .set_antenna_gain = ieee80211_set_antenna_gain,
|
|
.rfkill_poll = ieee80211_rfkill_poll,
|
|
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
|
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
|
|
@@ -4449,4 +4579,5 @@ const struct cfg80211_ops mac80211_config_ops = {
|
|
.reset_tid_config = ieee80211_reset_tid_config,
|
|
.set_sar_specs = ieee80211_set_sar_specs,
|
|
.color_change = ieee80211_color_change,
|
|
+ .set_radar_background = ieee80211_set_radar_background,
|
|
};
|
|
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
|
|
index a50f925..46f6c82 100644
|
|
--- a/net/mac80211/debugfs.c
|
|
+++ b/net/mac80211/debugfs.c
|
|
@@ -201,6 +201,36 @@ static const struct file_operations airtime_flags_ops = {
|
|
.llseek = default_llseek,
|
|
};
|
|
|
|
+static ssize_t aql_pending_read(struct file *file,
|
|
+ char __user *user_buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ieee80211_local *local = file->private_data;
|
|
+ char buf[400];
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf),
|
|
+ "AC AQL pending\n"
|
|
+ "VO %u us\n"
|
|
+ "VI %u us\n"
|
|
+ "BE %u us\n"
|
|
+ "BK %u us\n"
|
|
+ "total %u us\n",
|
|
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
|
|
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
|
|
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
|
|
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
|
|
+ atomic_read(&local->aql_total_pending_airtime));
|
|
+ return simple_read_from_buffer(user_buf, count, ppos,
|
|
+ buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations aql_pending_ops = {
|
|
+ .read = aql_pending_read,
|
|
+ .open = simple_open,
|
|
+ .llseek = default_llseek,
|
|
+};
|
|
+
|
|
static ssize_t aql_txq_limit_read(struct file *file,
|
|
char __user *user_buf,
|
|
size_t count,
|
|
@@ -216,14 +246,14 @@ static ssize_t aql_txq_limit_read(struct file *file,
|
|
"VI %u %u\n"
|
|
"BE %u %u\n"
|
|
"BK %u %u\n",
|
|
- 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);
|
|
+ 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]);
|
|
return simple_read_from_buffer(user_buf, count, ppos,
|
|
buf, len);
|
|
}
|
|
@@ -255,11 +285,11 @@ static ssize_t aql_txq_limit_write(struct file *file,
|
|
if (ac >= IEEE80211_NUM_ACS)
|
|
return -EINVAL;
|
|
|
|
- q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
|
|
- q_limit_high_old = local->airtime[ac].aql_txq_limit_high;
|
|
+ q_limit_low_old = local->aql_txq_limit_low[ac];
|
|
+ q_limit_high_old = local->aql_txq_limit_high[ac];
|
|
|
|
- local->airtime[ac].aql_txq_limit_low = q_limit_low;
|
|
- local->airtime[ac].aql_txq_limit_high = q_limit_high;
|
|
+ local->aql_txq_limit_low[ac] = q_limit_low;
|
|
+ local->aql_txq_limit_high[ac] = q_limit_high;
|
|
|
|
mutex_lock(&local->sta_mtx);
|
|
list_for_each_entry(sta, &local->sta_list, list) {
|
|
@@ -382,46 +412,6 @@ 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)
|
|
@@ -504,6 +494,7 @@ static const char *hw_flag_names[] = {
|
|
FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
|
|
FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
|
|
FLAG(SUPPORTS_CONC_MON_RX_DECAP),
|
|
+ FLAG(DETECTS_COLOR_COLLISION),
|
|
#undef FLAG
|
|
};
|
|
|
|
@@ -668,15 +659,12 @@ void debugfs_hw_add(struct ieee80211_local *local)
|
|
DEBUGFS_ADD(hw_conf);
|
|
DEBUGFS_ADD_MODE(force_tx_status, 0600);
|
|
DEBUGFS_ADD_MODE(aql_enable, 0600);
|
|
+ DEBUGFS_ADD(aql_pending);
|
|
|
|
if (local->ops->wake_tx_queue)
|
|
DEBUGFS_ADD_MODE(aqm, 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_MODE(airtime_flags, 0600);
|
|
|
|
DEBUGFS_ADD(aql_txq_limit);
|
|
debugfs_create_u32("aql_threshold", 0600,
|
|
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
|
|
index ac5e3ec..88e6e72 100644
|
|
--- a/net/mac80211/debugfs_netdev.c
|
|
+++ b/net/mac80211/debugfs_netdev.c
|
|
@@ -512,34 +512,6 @@ 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 */
|
|
@@ -685,10 +657,8 @@ 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)
|
|
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
|
|
index 8be28cf..afac8d4 100644
|
|
--- a/net/mac80211/debugfs_sta.c
|
|
+++ b/net/mac80211/debugfs_sta.c
|
|
@@ -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;
|
|
- u64 v_t[IEEE80211_NUM_ACS];
|
|
+ s32 deficit[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->airtime[ac].lock);
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
rx_airtime += sta->airtime[ac].rx_airtime;
|
|
tx_airtime += sta->airtime[ac].tx_airtime;
|
|
- v_t[ac] = sta->airtime[ac].v_t;
|
|
- spin_unlock_bh(&local->airtime[ac].lock);
|
|
+ deficit[ac] = sta->airtime[ac].deficit;
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
}
|
|
|
|
p += scnprintf(p, bufsz + buf - p,
|
|
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
|
|
- "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]);
|
|
+ "Deficit: VO: %d us VI: %d us BE: %d us BK: %d us\n",
|
|
+ rx_airtime, tx_airtime, sta->airtime_weight,
|
|
+ deficit[0], deficit[1], deficit[2], deficit[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->airtime[ac].lock);
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
sta->airtime[ac].rx_airtime = 0;
|
|
sta->airtime[ac].tx_airtime = 0;
|
|
- sta->airtime[ac].v_t = 0;
|
|
- spin_unlock_bh(&local->airtime[ac].lock);
|
|
+ sta->airtime[ac].deficit = sta->airtime_weight;
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
}
|
|
|
|
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->airtime[ac].lock);
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
|
|
q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
|
|
- spin_unlock_bh(&local->airtime[ac].lock);
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
|
|
}
|
|
|
|
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
|
|
index 912c75d..bd1e46e 100644
|
|
--- a/net/mac80211/driver-ops.h
|
|
+++ b/net/mac80211/driver-ops.h
|
|
@@ -1486,4 +1486,28 @@ static inline void drv_twt_teardown_request(struct ieee80211_local *local,
|
|
trace_drv_return_void(local);
|
|
}
|
|
|
|
+#if LINUX_VERSION_IS_GEQ(5,13,0)
|
|
+static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ struct ieee80211_sta *sta,
|
|
+ struct net_device_path_ctx *ctx,
|
|
+ struct net_device_path *path)
|
|
+{
|
|
+ int ret = -EOPNOTSUPP;
|
|
+
|
|
+ sdata = get_bss_sdata(sdata);
|
|
+ if (!check_sdata_in_driver(sdata))
|
|
+ return -EIO;
|
|
+
|
|
+ trace_drv_net_fill_forward_path(local, sdata, sta);
|
|
+ if (local->ops->net_fill_forward_path)
|
|
+ ret = local->ops->net_fill_forward_path(&local->hw,
|
|
+ &sdata->vif, sta,
|
|
+ ctx, path);
|
|
+ trace_drv_return_int(local, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif
|
|
+
|
|
#endif /* __MAC80211_DRIVER_OPS */
|
|
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
|
|
index 5d6ca4c..0416c4d 100644
|
|
--- a/net/mac80211/ibss.c
|
|
+++ b/net/mac80211/ibss.c
|
|
@@ -9,7 +9,7 @@
|
|
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
|
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
|
* Copyright(c) 2016 Intel Deutschland GmbH
|
|
- * Copyright(c) 2018-2020 Intel Corporation
|
|
+ * Copyright(c) 2018-2021 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
@@ -1589,7 +1589,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_rx_status *rx_status)
|
|
{
|
|
size_t baselen;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
|
|
BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) !=
|
|
offsetof(typeof(mgmt->u.beacon), variable));
|
|
@@ -1602,10 +1602,14 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata,
|
|
if (baselen > len)
|
|
return;
|
|
|
|
- ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
|
|
- false, &elems, mgmt->bssid, NULL);
|
|
+ elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
|
|
+ len - baselen, false,
|
|
+ mgmt->bssid, NULL);
|
|
|
|
- ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
|
+ if (elems) {
|
|
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, elems);
|
|
+ kfree(elems);
|
|
+ }
|
|
}
|
|
|
|
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
@@ -1614,7 +1618,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_rx_status *rx_status;
|
|
struct ieee80211_mgmt *mgmt;
|
|
u16 fc;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
int ies_len;
|
|
|
|
rx_status = IEEE80211_SKB_RXCB(skb);
|
|
@@ -1651,15 +1655,16 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
if (ies_len < 0)
|
|
break;
|
|
|
|
- ieee802_11_parse_elems(
|
|
+ elems = ieee802_11_parse_elems(
|
|
mgmt->u.action.u.chan_switch.variable,
|
|
- ies_len, true, &elems, mgmt->bssid, NULL);
|
|
-
|
|
- if (elems.parse_error)
|
|
- break;
|
|
-
|
|
- ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
|
|
- rx_status, &elems);
|
|
+ ies_len, true, mgmt->bssid, NULL);
|
|
+
|
|
+ if (elems && !elems->parse_error)
|
|
+ ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt,
|
|
+ skb->len,
|
|
+ rx_status,
|
|
+ elems);
|
|
+ kfree(elems);
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
|
|
index 5e9b0af..6edabd8 100644
|
|
--- a/net/mac80211/ieee80211_i.h
|
|
+++ b/net/mac80211/ieee80211_i.h
|
|
@@ -83,6 +83,15 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS];
|
|
|
|
#define IEEE80211_MAX_NAN_INSTANCE_ID 255
|
|
|
|
+
|
|
+/*
|
|
+ * Keep a station's queues on the active list for deficit accounting purposes
|
|
+ * if it was active or queued during the last 100ms
|
|
+ */
|
|
+#define AIRTIME_ACTIVE_DURATION (HZ / 10)
|
|
+
|
|
+#define AIRTIME_QUANTUM_SHIFT 3
|
|
+
|
|
struct ieee80211_bss {
|
|
u32 device_ts_beacon, device_ts_presp;
|
|
|
|
@@ -261,6 +270,7 @@ struct beacon_data {
|
|
struct ieee80211_meshconf_ie *meshconf;
|
|
u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
|
|
u8 cntdwn_current_counter;
|
|
+ struct cfg80211_mbssid_elems *mbssid_ies;
|
|
struct rcu_head rcu_head;
|
|
};
|
|
|
|
@@ -635,10 +645,9 @@ struct ieee80211_if_ocb {
|
|
*/
|
|
struct ieee802_11_elems;
|
|
struct ieee80211_mesh_sync_ops {
|
|
- void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata,
|
|
- u16 stype,
|
|
- struct ieee80211_mgmt *mgmt,
|
|
- struct ieee802_11_elems *elems,
|
|
+ void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, u16 stype,
|
|
+ struct ieee80211_mgmt *mgmt, unsigned int len,
|
|
+ const struct ieee80211_meshconf_ie *mesh_cfg,
|
|
struct ieee80211_rx_status *rx_status);
|
|
|
|
/* should be called with beacon_data under RCU read lock */
|
|
@@ -862,16 +871,20 @@ 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
|
|
- * @schedule_order: used with ieee80211_local->active_txqs
|
|
* @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
|
|
*/
|
|
struct txq_info {
|
|
struct fq_tin tin;
|
|
struct codel_vars def_cvars;
|
|
struct codel_stats cstats;
|
|
- struct rb_node schedule_order;
|
|
+
|
|
+ u16 schedule_round;
|
|
+ struct list_head schedule_order;
|
|
|
|
struct sk_buff_head frags;
|
|
+
|
|
unsigned long flags;
|
|
|
|
/* keep last! */
|
|
@@ -948,8 +961,6 @@ 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;
|
|
@@ -1083,6 +1094,20 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
|
|
return shift;
|
|
}
|
|
|
|
+static inline int
|
|
+ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems)
|
|
+{
|
|
+ int i, len = 0;
|
|
+
|
|
+ if (!elems)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < elems->cnt; i++)
|
|
+ len += elems->elem[i].len;
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
enum {
|
|
IEEE80211_RX_MSG = 1,
|
|
IEEE80211_TX_STATUS_MSG = 2,
|
|
@@ -1170,44 +1195,6 @@ 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 {
|
|
@@ -1221,10 +1208,16 @@ struct ieee80211_local {
|
|
struct codel_params cparams;
|
|
|
|
/* protects active_txqs and txqi->schedule_order */
|
|
- struct airtime_sched_info airtime[IEEE80211_NUM_ACS];
|
|
+ spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
|
|
+ struct list_head active_txqs[IEEE80211_NUM_ACS];
|
|
+ u16 schedule_round[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;
|
|
+ atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
|
|
|
|
const struct ieee80211_ops *ops;
|
|
|
|
@@ -1454,6 +1447,7 @@ struct ieee80211_local {
|
|
int dynamic_ps_forced_timeout;
|
|
|
|
int user_power_level; /* in dBm, for all interfaces */
|
|
+ int user_antenna_gain; /* in dBi */
|
|
|
|
enum ieee80211_smps_mode smps_mode;
|
|
|
|
@@ -1490,7 +1484,7 @@ struct ieee80211_local {
|
|
};
|
|
|
|
static inline struct ieee80211_sub_if_data *
|
|
-IEEE80211_DEV_TO_SUB_IF(struct net_device *dev)
|
|
+IEEE80211_DEV_TO_SUB_IF(const struct net_device *dev)
|
|
{
|
|
return netdev_priv(dev);
|
|
}
|
|
@@ -1537,6 +1531,7 @@ struct ieee80211_csa_ie {
|
|
struct ieee802_11_elems {
|
|
const u8 *ie_start;
|
|
size_t total_len;
|
|
+ u32 crc;
|
|
|
|
/* pointers to IEs */
|
|
const struct ieee80211_tdls_lnkie *lnk_id;
|
|
@@ -1546,7 +1541,6 @@ struct ieee802_11_elems {
|
|
const u8 *supp_rates;
|
|
const u8 *ds_params;
|
|
const struct ieee80211_tim_ie *tim;
|
|
- const u8 *challenge;
|
|
const u8 *rsn;
|
|
const u8 *rsnx;
|
|
const u8 *erp_info;
|
|
@@ -1600,7 +1594,6 @@ struct ieee802_11_elems {
|
|
u8 ssid_len;
|
|
u8 supp_rates_len;
|
|
u8 tim_len;
|
|
- u8 challenge_len;
|
|
u8 rsn_len;
|
|
u8 rsnx_len;
|
|
u8 ext_supp_rates_len;
|
|
@@ -1619,6 +1612,14 @@ struct ieee802_11_elems {
|
|
|
|
/* whether a parse error occurred while retrieving these elements */
|
|
bool parse_error;
|
|
+
|
|
+ /*
|
|
+ * scratch buffer that can be used for various element parsing related
|
|
+ * tasks, e.g., element de-fragmentation etc.
|
|
+ */
|
|
+ size_t scratch_len;
|
|
+ u8 *scratch_pos;
|
|
+ u8 scratch[];
|
|
};
|
|
|
|
static inline struct ieee80211_local *hw_to_local(
|
|
@@ -1639,125 +1640,6 @@ 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) ||
|
|
@@ -2003,14 +1885,6 @@ 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,
|
|
@@ -2223,18 +2097,18 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
|
|
ieee80211_tx_skb_tid(sdata, skb, 7);
|
|
}
|
|
|
|
-u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
|
- struct ieee802_11_elems *elems,
|
|
- u64 filter, u32 crc, u8 *transmitter_bssid,
|
|
- u8 *bss_bssid);
|
|
-static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
|
|
- bool action,
|
|
- struct ieee802_11_elems *elems,
|
|
- u8 *transmitter_bssid,
|
|
- u8 *bss_bssid)
|
|
+struct ieee802_11_elems *ieee802_11_parse_elems_crc(const u8 *start, size_t len,
|
|
+ bool action,
|
|
+ u64 filter, u32 crc,
|
|
+ const u8 *transmitter_bssid,
|
|
+ const u8 *bss_bssid);
|
|
+static inline struct ieee802_11_elems *
|
|
+ieee802_11_parse_elems(const u8 *start, size_t len, bool action,
|
|
+ const u8 *transmitter_bssid,
|
|
+ const u8 *bss_bssid)
|
|
{
|
|
- ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0,
|
|
- transmitter_bssid, bss_bssid);
|
|
+ return ieee802_11_parse_elems_crc(start, len, action, 0, 0,
|
|
+ transmitter_bssid, bss_bssid);
|
|
}
|
|
|
|
|
|
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
|
|
index b6dc214..dede578 100644
|
|
--- a/net/mac80211/iface.c
|
|
+++ b/net/mac80211/iface.c
|
|
@@ -378,6 +378,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
|
|
struct cfg80211_nan_func *func;
|
|
|
|
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
|
+ synchronize_rcu(); /* flush _ieee80211_wake_txqs() */
|
|
|
|
cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
|
|
if (cancel_scan)
|
|
@@ -632,17 +633,46 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
|
|
ieee80211_add_virtual_monitor(local);
|
|
}
|
|
|
|
+static void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
|
|
+{
|
|
+ struct ieee80211_sub_if_data *tx_sdata, *non_tx_sdata, *tmp_sdata;
|
|
+ struct ieee80211_vif *tx_vif = sdata->vif.mbssid_tx_vif;
|
|
+
|
|
+ if (!tx_vif)
|
|
+ return;
|
|
+
|
|
+ tx_sdata = vif_to_sdata(tx_vif);
|
|
+ sdata->vif.mbssid_tx_vif = NULL;
|
|
+
|
|
+ list_for_each_entry_safe(non_tx_sdata, tmp_sdata,
|
|
+ &tx_sdata->local->interfaces, list) {
|
|
+ if (non_tx_sdata != sdata && non_tx_sdata != tx_sdata &&
|
|
+ non_tx_sdata->vif.mbssid_tx_vif == tx_vif &&
|
|
+ ieee80211_sdata_running(non_tx_sdata)) {
|
|
+ non_tx_sdata->vif.mbssid_tx_vif = NULL;
|
|
+ dev_close(non_tx_sdata->wdev.netdev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sdata != tx_sdata && ieee80211_sdata_running(tx_sdata)) {
|
|
+ tx_sdata->vif.mbssid_tx_vif = NULL;
|
|
+ dev_close(tx_sdata->wdev.netdev);
|
|
+ }
|
|
+}
|
|
+
|
|
static int ieee80211_stop(struct net_device *dev)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
- /* close all dependent VLAN interfaces before locking wiphy */
|
|
+ /* close dependent VLAN and MBSSID interfaces before locking wiphy */
|
|
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
|
struct ieee80211_sub_if_data *vlan, *tmpsdata;
|
|
|
|
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
|
|
u.vlan.list)
|
|
dev_close(vlan->dev);
|
|
+
|
|
+ ieee80211_stop_mbssid(sdata);
|
|
}
|
|
|
|
wiphy_lock(sdata->local->hw.wiphy);
|
|
@@ -822,6 +852,66 @@ static const struct net_device_ops ieee80211_monitorif_ops = {
|
|
|
|
};
|
|
|
|
+#if LINUX_VERSION_IS_GEQ(5,13,0)
|
|
+static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
|
|
+ struct net_device_path *path)
|
|
+{
|
|
+ struct ieee80211_sub_if_data *sdata;
|
|
+ struct ieee80211_local *local;
|
|
+ struct sta_info *sta;
|
|
+ int ret = -ENOENT;
|
|
+
|
|
+ sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
|
|
+ local = sdata->local;
|
|
+
|
|
+ if (!local->ops->net_fill_forward_path)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ switch (sdata->vif.type) {
|
|
+ case NL80211_IFTYPE_AP_VLAN:
|
|
+ sta = rcu_dereference(sdata->u.vlan.sta);
|
|
+ if (sta)
|
|
+ break;
|
|
+ if (sdata->wdev.use_4addr)
|
|
+ goto out;
|
|
+ if (is_multicast_ether_addr(ctx->daddr))
|
|
+ goto out;
|
|
+ sta = sta_info_get_bss(sdata, ctx->daddr);
|
|
+ break;
|
|
+ case NL80211_IFTYPE_AP:
|
|
+ if (is_multicast_ether_addr(ctx->daddr))
|
|
+ goto out;
|
|
+ sta = sta_info_get(sdata, ctx->daddr);
|
|
+ break;
|
|
+ case NL80211_IFTYPE_STATION:
|
|
+ if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
|
|
+ sta = sta_info_get(sdata, ctx->daddr);
|
|
+ if (sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
|
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
|
|
+ goto out;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sta = sta_info_get(sdata, sdata->u.mgd.bssid);
|
|
+ break;
|
|
+ default:
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (!sta)
|
|
+ goto out;
|
|
+
|
|
+ ret = drv_net_fill_forward_path(local, sdata, &sta->sta, ctx, path);
|
|
+out:
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif
|
|
+
|
|
static const struct net_device_ops ieee80211_dataif_8023_ops = {
|
|
#if LINUX_VERSION_IS_LESS(4,10,0)
|
|
.ndo_change_mtu = __change_mtu,
|
|
@@ -839,7 +929,9 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
|
|
#else
|
|
.ndo_get_stats64 = bp_ieee80211_get_stats64,
|
|
#endif
|
|
-
|
|
+#if LINUX_VERSION_IS_GEQ(5,13,0)
|
|
+ .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
|
|
+#endif
|
|
};
|
|
|
|
static bool ieee80211_iftype_supports_hdr_offload(enum nl80211_iftype iftype)
|
|
@@ -2099,9 +2191,6 @@ 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;
|
|
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
|
|
index 397d289..09e5bf1 100644
|
|
--- a/net/mac80211/main.c
|
|
+++ b/net/mac80211/main.c
|
|
@@ -96,7 +96,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct cfg80211_chan_def chandef = {};
|
|
u32 changed = 0;
|
|
- int power;
|
|
+ int power, max_power;
|
|
u32 offchannel_flag;
|
|
|
|
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
|
|
@@ -157,6 +157,12 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
+ max_power = chandef.chan->max_reg_power;
|
|
+ if (local->user_antenna_gain > 0) {
|
|
+ max_power -= local->user_antenna_gain;
|
|
+ power = min(power, max_power);
|
|
+ }
|
|
+
|
|
if (local->hw.conf.power_level != power) {
|
|
changed |= IEEE80211_CONF_CHANGE_POWER;
|
|
local->hw.conf.power_level = power;
|
|
@@ -337,7 +343,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_restart_hw);
|
|
|
|
-#ifdef CONFIG_INET
|
|
+#ifdef __disabled__CONFIG_INET
|
|
static int ieee80211_ifa_changed(struct notifier_block *nb,
|
|
unsigned long data, void *arg)
|
|
{
|
|
@@ -396,7 +402,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
|
|
}
|
|
#endif
|
|
|
|
-#if IS_ENABLED(CONFIG_IPV6)
|
|
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
|
static int ieee80211_ifa6_changed(struct notifier_block *nb,
|
|
unsigned long data, void *arg)
|
|
{
|
|
@@ -679,6 +685,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
|
IEEE80211_RADIOTAP_MCS_HAVE_BW;
|
|
local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
|
|
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
|
|
+ local->user_antenna_gain = 0;
|
|
local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
|
|
local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
|
|
local->hw.max_mtu = IEEE80211_MAX_DATA_LEN;
|
|
@@ -707,14 +714,12 @@ 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++) {
|
|
- 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 =
|
|
+ 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] =
|
|
IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
|
|
+ atomic_set(&local->aql_ac_pending_airtime[i], 0);
|
|
}
|
|
|
|
local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
|
|
@@ -1321,14 +1326,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
wiphy_unlock(hw->wiphy);
|
|
rtnl_unlock();
|
|
|
|
-#ifdef CONFIG_INET
|
|
+#ifdef __disabled__CONFIG_INET
|
|
local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
|
|
result = register_inetaddr_notifier(&local->ifa_notifier);
|
|
if (result)
|
|
goto fail_ifa;
|
|
#endif
|
|
|
|
-#if IS_ENABLED(CONFIG_IPV6)
|
|
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
|
local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
|
|
result = register_inet6addr_notifier(&local->ifa6_notifier);
|
|
if (result)
|
|
@@ -1337,13 +1342,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
|
|
return 0;
|
|
|
|
-#if IS_ENABLED(CONFIG_IPV6)
|
|
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
|
fail_ifa6:
|
|
-#ifdef CONFIG_INET
|
|
+#ifdef __disabled__CONFIG_INET
|
|
unregister_inetaddr_notifier(&local->ifa_notifier);
|
|
#endif
|
|
#endif
|
|
-#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
|
|
+#if defined(__disabled__CONFIG_INET) || defined(__disabled__CONFIG_IPV6)
|
|
fail_ifa:
|
|
#endif
|
|
wiphy_unregister(local->hw.wiphy);
|
|
@@ -1371,10 +1376,10 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
|
tasklet_kill(&local->tx_pending_tasklet);
|
|
tasklet_kill(&local->tasklet);
|
|
|
|
-#ifdef CONFIG_INET
|
|
+#ifdef __disabled__CONFIG_INET
|
|
unregister_inetaddr_notifier(&local->ifa_notifier);
|
|
#endif
|
|
-#if IS_ENABLED(CONFIG_IPV6)
|
|
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
|
unregister_inet6addr_notifier(&local->ifa6_notifier);
|
|
#endif
|
|
|
|
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
|
|
index 42bd81a..6847fdf 100644
|
|
--- a/net/mac80211/mesh.c
|
|
+++ b/net/mac80211/mesh.c
|
|
@@ -1247,7 +1247,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
|
|
struct sk_buff *presp;
|
|
struct beacon_data *bcn;
|
|
struct ieee80211_mgmt *hdr;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
size_t baselen;
|
|
u8 *pos;
|
|
|
|
@@ -1256,22 +1256,24 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
|
|
if (baselen > len)
|
|
return;
|
|
|
|
- ieee802_11_parse_elems(pos, len - baselen, false, &elems, mgmt->bssid,
|
|
- NULL);
|
|
-
|
|
- if (!elems.mesh_id)
|
|
+ elems = ieee802_11_parse_elems(pos, len - baselen, false, mgmt->bssid,
|
|
+ NULL);
|
|
+ if (!elems)
|
|
return;
|
|
|
|
+ if (!elems->mesh_id)
|
|
+ goto free;
|
|
+
|
|
/* 802.11-2012 10.1.4.3.2 */
|
|
if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
|
|
!is_broadcast_ether_addr(mgmt->da)) ||
|
|
- elems.ssid_len != 0)
|
|
- return;
|
|
+ elems->ssid_len != 0)
|
|
+ goto free;
|
|
|
|
- if (elems.mesh_id_len != 0 &&
|
|
- (elems.mesh_id_len != ifmsh->mesh_id_len ||
|
|
- memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
|
|
- return;
|
|
+ if (elems->mesh_id_len != 0 &&
|
|
+ (elems->mesh_id_len != ifmsh->mesh_id_len ||
|
|
+ memcmp(elems->mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
|
|
+ goto free;
|
|
|
|
rcu_read_lock();
|
|
bcn = rcu_dereference(ifmsh->beacon);
|
|
@@ -1295,6 +1297,8 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
|
|
ieee80211_tx_skb(sdata, presp);
|
|
out:
|
|
rcu_read_unlock();
|
|
+free:
|
|
+ kfree(elems);
|
|
}
|
|
|
|
static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
|
@@ -1305,7 +1309,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
|
{
|
|
struct ieee80211_local *local = sdata->local;
|
|
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
struct ieee80211_channel *channel;
|
|
size_t baselen;
|
|
int freq;
|
|
@@ -1320,42 +1324,47 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
|
if (baselen > len)
|
|
return;
|
|
|
|
- ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
|
|
- false, &elems, mgmt->bssid, NULL);
|
|
+ elems = ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
|
|
+ len - baselen,
|
|
+ false, mgmt->bssid, NULL);
|
|
+ if (!elems)
|
|
+ return;
|
|
|
|
/* ignore non-mesh or secure / unsecure mismatch */
|
|
- if ((!elems.mesh_id || !elems.mesh_config) ||
|
|
- (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
|
|
- (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
|
|
- return;
|
|
+ if ((!elems->mesh_id || !elems->mesh_config) ||
|
|
+ (elems->rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
|
|
+ (!elems->rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
|
|
+ goto free;
|
|
|
|
- if (elems.ds_params)
|
|
- freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);
|
|
+ if (elems->ds_params)
|
|
+ freq = ieee80211_channel_to_frequency(elems->ds_params[0], band);
|
|
else
|
|
freq = rx_status->freq;
|
|
|
|
channel = ieee80211_get_channel(local->hw.wiphy, freq);
|
|
|
|
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
|
|
- return;
|
|
+ goto free;
|
|
|
|
- if (mesh_matches_local(sdata, &elems)) {
|
|
+ if (mesh_matches_local(sdata, elems)) {
|
|
mpl_dbg(sdata, "rssi_threshold=%d,rx_status->signal=%d\n",
|
|
sdata->u.mesh.mshcfg.rssi_threshold, rx_status->signal);
|
|
if (!sdata->u.mesh.user_mpm ||
|
|
sdata->u.mesh.mshcfg.rssi_threshold == 0 ||
|
|
sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
|
|
- mesh_neighbour_update(sdata, mgmt->sa, &elems,
|
|
+ mesh_neighbour_update(sdata, mgmt->sa, elems,
|
|
rx_status);
|
|
|
|
if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
|
|
!sdata->vif.csa_active)
|
|
- ieee80211_mesh_process_chnswitch(sdata, &elems, true);
|
|
+ ieee80211_mesh_process_chnswitch(sdata, elems, true);
|
|
}
|
|
|
|
if (ifmsh->sync_ops)
|
|
- ifmsh->sync_ops->rx_bcn_presp(sdata,
|
|
- stype, mgmt, &elems, rx_status);
|
|
+ ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, len,
|
|
+ elems->mesh_config, rx_status);
|
|
+free:
|
|
+ kfree(elems);
|
|
}
|
|
|
|
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
|
|
@@ -1447,7 +1456,7 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
|
{
|
|
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
u16 pre_value;
|
|
bool fwd_csa = true;
|
|
size_t baselen;
|
|
@@ -1460,33 +1469,37 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
|
|
pos = mgmt->u.action.u.chan_switch.variable;
|
|
baselen = offsetof(struct ieee80211_mgmt,
|
|
u.action.u.chan_switch.variable);
|
|
- ieee802_11_parse_elems(pos, len - baselen, true, &elems,
|
|
- mgmt->bssid, NULL);
|
|
-
|
|
- if (!mesh_matches_local(sdata, &elems))
|
|
+ elems = ieee802_11_parse_elems(pos, len - baselen, true,
|
|
+ mgmt->bssid, NULL);
|
|
+ if (!elems)
|
|
return;
|
|
|
|
- ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
|
|
+ if (!mesh_matches_local(sdata, elems))
|
|
+ goto free;
|
|
+
|
|
+ ifmsh->chsw_ttl = elems->mesh_chansw_params_ie->mesh_ttl;
|
|
if (!--ifmsh->chsw_ttl)
|
|
fwd_csa = false;
|
|
|
|
- pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
|
|
+ pre_value = le16_to_cpu(elems->mesh_chansw_params_ie->mesh_pre_value);
|
|
if (ifmsh->pre_value >= pre_value)
|
|
- return;
|
|
+ goto free;
|
|
|
|
ifmsh->pre_value = pre_value;
|
|
|
|
if (!sdata->vif.csa_active &&
|
|
- !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
|
|
+ !ieee80211_mesh_process_chnswitch(sdata, elems, false)) {
|
|
mcsa_dbg(sdata, "Failed to process CSA action frame");
|
|
- return;
|
|
+ goto free;
|
|
}
|
|
|
|
/* forward or re-broadcast the CSA frame */
|
|
if (fwd_csa) {
|
|
- if (mesh_fwd_csa_frame(sdata, mgmt, len, &elems) < 0)
|
|
+ if (mesh_fwd_csa_frame(sdata, mgmt, len, elems) < 0)
|
|
mcsa_dbg(sdata, "Failed to forward the CSA frame");
|
|
}
|
|
+free:
|
|
+ kfree(elems);
|
|
}
|
|
|
|
static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
|
|
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
|
|
index a05b615..44a6fdb 100644
|
|
--- a/net/mac80211/mesh_hwmp.c
|
|
+++ b/net/mac80211/mesh_hwmp.c
|
|
@@ -1,7 +1,7 @@
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2008, 2009 open80211s Ltd.
|
|
- * Copyright (C) 2019 Intel Corporation
|
|
+ * Copyright (C) 2019, 2021 Intel Corporation
|
|
* Author: Luis Carlos Cobo <luisca@cozybit.com>
|
|
*/
|
|
|
|
@@ -908,7 +908,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
|
|
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
|
{
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
size_t baselen;
|
|
u32 path_metric;
|
|
struct sta_info *sta;
|
|
@@ -926,37 +926,41 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
|
rcu_read_unlock();
|
|
|
|
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
|
|
- ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
|
|
- len - baselen, false, &elems, mgmt->bssid, NULL);
|
|
+ elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
|
|
+ len - baselen, false, mgmt->bssid, NULL);
|
|
+ if (!elems)
|
|
+ return;
|
|
|
|
- if (elems.preq) {
|
|
- if (elems.preq_len != 37)
|
|
+ if (elems->preq) {
|
|
+ if (elems->preq_len != 37)
|
|
/* Right now we support just 1 destination and no AE */
|
|
- return;
|
|
- path_metric = hwmp_route_info_get(sdata, mgmt, elems.preq,
|
|
+ goto free;
|
|
+ path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
|
|
MPATH_PREQ);
|
|
if (path_metric)
|
|
- hwmp_preq_frame_process(sdata, mgmt, elems.preq,
|
|
+ hwmp_preq_frame_process(sdata, mgmt, elems->preq,
|
|
path_metric);
|
|
}
|
|
- if (elems.prep) {
|
|
- if (elems.prep_len != 31)
|
|
+ if (elems->prep) {
|
|
+ if (elems->prep_len != 31)
|
|
/* Right now we support no AE */
|
|
- return;
|
|
- path_metric = hwmp_route_info_get(sdata, mgmt, elems.prep,
|
|
+ goto free;
|
|
+ path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
|
|
MPATH_PREP);
|
|
if (path_metric)
|
|
- hwmp_prep_frame_process(sdata, mgmt, elems.prep,
|
|
+ hwmp_prep_frame_process(sdata, mgmt, elems->prep,
|
|
path_metric);
|
|
}
|
|
- if (elems.perr) {
|
|
- if (elems.perr_len != 15)
|
|
+ if (elems->perr) {
|
|
+ if (elems->perr_len != 15)
|
|
/* Right now we support only one destination per PERR */
|
|
- return;
|
|
- hwmp_perr_frame_process(sdata, mgmt, elems.perr);
|
|
+ goto free;
|
|
+ hwmp_perr_frame_process(sdata, mgmt, elems->perr);
|
|
}
|
|
- if (elems.rann)
|
|
- hwmp_rann_frame_process(sdata, mgmt, elems.rann);
|
|
+ if (elems->rann)
|
|
+ hwmp_rann_frame_process(sdata, mgmt, elems->rann);
|
|
+free:
|
|
+ kfree(elems);
|
|
}
|
|
|
|
/**
|
|
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
|
|
index a691584..a829470 100644
|
|
--- a/net/mac80211/mesh_plink.c
|
|
+++ b/net/mac80211/mesh_plink.c
|
|
@@ -1,7 +1,7 @@
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2008, 2009 open80211s Ltd.
|
|
- * Copyright (C) 2019 Intel Corporation
|
|
+ * Copyright (C) 2019, 2021 Intel Corporation
|
|
* Author: Luis Carlos Cobo <luisca@cozybit.com>
|
|
*/
|
|
#include <linux/gfp.h>
|
|
@@ -1200,7 +1200,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_mgmt *mgmt, size_t len,
|
|
struct ieee80211_rx_status *rx_status)
|
|
{
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
size_t baselen;
|
|
u8 *baseaddr;
|
|
|
|
@@ -1228,7 +1228,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
|
|
if (baselen > len)
|
|
return;
|
|
}
|
|
- ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems,
|
|
- mgmt->bssid, NULL);
|
|
- mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
|
|
+ elems = ieee802_11_parse_elems(baseaddr, len - baselen, true,
|
|
+ mgmt->bssid, NULL);
|
|
+ mesh_process_plink_frame(sdata, mgmt, elems, rx_status);
|
|
+ kfree(elems);
|
|
}
|
|
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
|
|
index fde93de..9e342cc 100644
|
|
--- a/net/mac80211/mesh_sync.c
|
|
+++ b/net/mac80211/mesh_sync.c
|
|
@@ -3,6 +3,7 @@
|
|
* Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com>
|
|
* Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
|
|
* Copyright 2011-2012, cozybit Inc.
|
|
+ * Copyright (C) 2021 Intel Corporation
|
|
*/
|
|
|
|
#include "ieee80211_i.h"
|
|
@@ -35,12 +36,12 @@ struct sync_method {
|
|
/**
|
|
* mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT
|
|
*
|
|
- * @ie: information elements of a management frame from the mesh peer
|
|
+ * @cfg: mesh config element from the mesh peer (or %NULL)
|
|
*/
|
|
-static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
|
|
+static bool mesh_peer_tbtt_adjusting(const struct ieee80211_meshconf_ie *cfg)
|
|
{
|
|
- return (ie->mesh_config->meshconf_cap &
|
|
- IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
|
|
+ return cfg &&
|
|
+ (cfg->meshconf_cap & IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING);
|
|
}
|
|
|
|
void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata)
|
|
@@ -76,11 +77,11 @@ void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata)
|
|
}
|
|
}
|
|
|
|
-static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
|
- u16 stype,
|
|
- struct ieee80211_mgmt *mgmt,
|
|
- struct ieee802_11_elems *elems,
|
|
- struct ieee80211_rx_status *rx_status)
|
|
+static void
|
|
+mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype,
|
|
+ struct ieee80211_mgmt *mgmt, unsigned int len,
|
|
+ const struct ieee80211_meshconf_ie *mesh_cfg,
|
|
+ struct ieee80211_rx_status *rx_status)
|
|
{
|
|
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
|
struct ieee80211_local *local = sdata->local;
|
|
@@ -101,10 +102,7 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
|
*/
|
|
if (ieee80211_have_rx_timestamp(rx_status))
|
|
t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
|
|
- 24 + 12 +
|
|
- elems->total_len +
|
|
- FCS_LEN,
|
|
- 24);
|
|
+ len + FCS_LEN, 24);
|
|
else
|
|
t_r = drv_get_tsf(local, sdata);
|
|
|
|
@@ -119,7 +117,7 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
|
* dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors
|
|
*/
|
|
|
|
- if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
|
|
+ if (mesh_peer_tbtt_adjusting(mesh_cfg)) {
|
|
msync_dbg(sdata, "STA %pM : is adjusting TBTT\n",
|
|
sta->sta.addr);
|
|
goto no_sync;
|
|
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
|
|
index 1548f53..cc6d38a 100644
|
|
--- a/net/mac80211/mlme.c
|
|
+++ b/net/mac80211/mlme.c
|
|
@@ -2889,17 +2889,17 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
|
{
|
|
struct ieee80211_local *local = sdata->local;
|
|
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
|
+ const struct element *challenge;
|
|
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,
|
|
- mgmt->bssid, auth_data->bss->bssid);
|
|
- if (!elems.challenge)
|
|
+ challenge = cfg80211_find_elem(WLAN_EID_CHALLENGE, pos,
|
|
+ len - (pos - (u8 *)mgmt));
|
|
+ if (!challenge)
|
|
return;
|
|
auth_data->expected_transaction = 4;
|
|
drv_mgd_prepare_tx(sdata->local, sdata, &info);
|
|
@@ -2907,7 +2907,8 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
|
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
|
ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
|
|
- elems.challenge - 2, elems.challenge_len + 2,
|
|
+ (void *)challenge,
|
|
+ challenge->datalen + sizeof(*challenge),
|
|
auth_data->bss->bssid, auth_data->bss->bssid,
|
|
auth_data->key, auth_data->key_len,
|
|
auth_data->key_idx, tx_flags);
|
|
@@ -3316,8 +3317,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
aid = 0; /* TODO */
|
|
}
|
|
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
|
|
- ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, elems,
|
|
- mgmt->bssid, assoc_data->bss->bssid);
|
|
+ elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
|
|
+ mgmt->bssid, assoc_data->bss->bssid);
|
|
+
|
|
+ if (!elems)
|
|
+ return false;
|
|
|
|
if (elems->aid_resp)
|
|
aid = le16_to_cpu(elems->aid_resp->aid);
|
|
@@ -3339,7 +3343,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
|
if (!is_s1g && !elems->supp_rates) {
|
|
sdata_info(sdata, "no SuppRates element in AssocResp\n");
|
|
- return false;
|
|
+ ret = false;
|
|
+ goto out;
|
|
}
|
|
|
|
sdata->vif.bss_conf.aid = aid;
|
|
@@ -3361,7 +3366,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
(!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
(!elems->vht_cap_elem || !elems->vht_operation)))) {
|
|
const struct cfg80211_bss_ies *ies;
|
|
- struct ieee802_11_elems bss_elems;
|
|
+ struct ieee802_11_elems *bss_elems;
|
|
|
|
rcu_read_lock();
|
|
ies = rcu_dereference(cbss->ies);
|
|
@@ -3369,16 +3374,22 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
bss_ies = kmemdup(ies, sizeof(*ies) + ies->len,
|
|
GFP_ATOMIC);
|
|
rcu_read_unlock();
|
|
- if (!bss_ies)
|
|
- return false;
|
|
+ if (!bss_ies) {
|
|
+ ret = false;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
|
|
+ false, mgmt->bssid,
|
|
+ assoc_data->bss->bssid);
|
|
+ if (!bss_elems) {
|
|
+ ret = false;
|
|
+ goto out;
|
|
+ }
|
|
|
|
- ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
|
|
- false, &bss_elems,
|
|
- mgmt->bssid,
|
|
- assoc_data->bss->bssid);
|
|
if (assoc_data->wmm &&
|
|
- !elems->wmm_param && bss_elems.wmm_param) {
|
|
- elems->wmm_param = bss_elems.wmm_param;
|
|
+ !elems->wmm_param && bss_elems->wmm_param) {
|
|
+ elems->wmm_param = bss_elems->wmm_param;
|
|
sdata_info(sdata,
|
|
"AP bug: WMM param missing from AssocResp\n");
|
|
}
|
|
@@ -3387,30 +3398,32 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
* Also check if we requested HT/VHT, otherwise the AP doesn't
|
|
* have to include the IEs in the (re)association response.
|
|
*/
|
|
- if (!elems->ht_cap_elem && bss_elems.ht_cap_elem &&
|
|
+ if (!elems->ht_cap_elem && bss_elems->ht_cap_elem &&
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
|
- elems->ht_cap_elem = bss_elems.ht_cap_elem;
|
|
+ elems->ht_cap_elem = bss_elems->ht_cap_elem;
|
|
sdata_info(sdata,
|
|
"AP bug: HT capability missing from AssocResp\n");
|
|
}
|
|
- if (!elems->ht_operation && bss_elems.ht_operation &&
|
|
+ if (!elems->ht_operation && bss_elems->ht_operation &&
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
|
- elems->ht_operation = bss_elems.ht_operation;
|
|
+ elems->ht_operation = bss_elems->ht_operation;
|
|
sdata_info(sdata,
|
|
"AP bug: HT operation missing from AssocResp\n");
|
|
}
|
|
- if (!elems->vht_cap_elem && bss_elems.vht_cap_elem &&
|
|
+ if (!elems->vht_cap_elem && bss_elems->vht_cap_elem &&
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
|
|
- elems->vht_cap_elem = bss_elems.vht_cap_elem;
|
|
+ elems->vht_cap_elem = bss_elems->vht_cap_elem;
|
|
sdata_info(sdata,
|
|
"AP bug: VHT capa missing from AssocResp\n");
|
|
}
|
|
- if (!elems->vht_operation && bss_elems.vht_operation &&
|
|
+ if (!elems->vht_operation && bss_elems->vht_operation &&
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
|
|
- elems->vht_operation = bss_elems.vht_operation;
|
|
+ elems->vht_operation = bss_elems->vht_operation;
|
|
sdata_info(sdata,
|
|
"AP bug: VHT operation missing from AssocResp\n");
|
|
}
|
|
+
|
|
+ kfree(bss_elems);
|
|
}
|
|
|
|
/*
|
|
@@ -3661,6 +3674,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
|
ret = true;
|
|
out:
|
|
+ kfree(elems);
|
|
kfree(bss_ies);
|
|
return ret;
|
|
}
|
|
@@ -3672,7 +3686,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
|
u16 capab_info, status_code, aid;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
int ac, uapsd_queues = -1;
|
|
u8 *pos;
|
|
bool reassoc;
|
|
@@ -3729,14 +3743,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
|
|
return;
|
|
|
|
- ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
|
|
- mgmt->bssid, assoc_data->bss->bssid);
|
|
+ elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
|
|
+ mgmt->bssid, assoc_data->bss->bssid);
|
|
+ if (!elems)
|
|
+ goto notify_driver;
|
|
|
|
if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
|
|
- elems.timeout_int &&
|
|
- elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
|
|
+ elems->timeout_int &&
|
|
+ elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
|
|
u32 tu, ms;
|
|
- tu = le32_to_cpu(elems.timeout_int->value);
|
|
+ tu = le32_to_cpu(elems->timeout_int->value);
|
|
ms = tu * 1024 / 1000;
|
|
sdata_info(sdata,
|
|
"%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
|
|
@@ -3756,7 +3772,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
event.u.mlme.reason = status_code;
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
} else {
|
|
- if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, &elems)) {
|
|
+ if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, elems)) {
|
|
/* oops -- internal error -- send timeout for now */
|
|
ieee80211_destroy_assoc_data(sdata, false, false);
|
|
cfg80211_assoc_timeout(sdata->dev, cbss);
|
|
@@ -3786,6 +3802,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
ifmgd->assoc_req_ies, ifmgd->assoc_req_ies_len);
|
|
notify_driver:
|
|
drv_mgd_complete_tx(sdata->local, sdata, &info);
|
|
+ kfree(elems);
|
|
}
|
|
|
|
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
|
@@ -3990,7 +4007,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
|
struct ieee80211_mgmt *mgmt = (void *) hdr;
|
|
size_t baselen;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
struct ieee80211_local *local = sdata->local;
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
struct ieee80211_channel *chan;
|
|
@@ -4036,15 +4053,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
|
|
ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) {
|
|
- ieee802_11_parse_elems(variable,
|
|
- len - baselen, false, &elems,
|
|
- bssid,
|
|
- ifmgd->assoc_data->bss->bssid);
|
|
+ elems = ieee802_11_parse_elems(variable, len - baselen, false,
|
|
+ bssid,
|
|
+ ifmgd->assoc_data->bss->bssid);
|
|
+ if (!elems)
|
|
+ return;
|
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
|
|
|
|
- if (elems.dtim_period)
|
|
- ifmgd->dtim_period = elems.dtim_period;
|
|
+ if (elems->dtim_period)
|
|
+ ifmgd->dtim_period = elems->dtim_period;
|
|
ifmgd->have_beacon = true;
|
|
ifmgd->assoc_data->need_beacon = false;
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
|
|
@@ -4052,17 +4070,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
rx_status->device_timestamp;
|
|
- sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
|
|
+ sdata->vif.bss_conf.sync_dtim_count = elems->dtim_count;
|
|
}
|
|
|
|
- if (elems.mbssid_config_ie)
|
|
+ if (elems->mbssid_config_ie)
|
|
bss_conf->profile_periodicity =
|
|
- elems.mbssid_config_ie->profile_periodicity;
|
|
+ elems->mbssid_config_ie->profile_periodicity;
|
|
else
|
|
bss_conf->profile_periodicity = 0;
|
|
|
|
- if (elems.ext_capab_len >= 11 &&
|
|
- (elems.ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
|
|
+ if (elems->ext_capab_len >= 11 &&
|
|
+ (elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
|
|
bss_conf->ema_ap = true;
|
|
else
|
|
bss_conf->ema_ap = false;
|
|
@@ -4071,6 +4089,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
ifmgd->assoc_data->timeout = jiffies;
|
|
ifmgd->assoc_data->timeout_started = true;
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
|
+ kfree(elems);
|
|
return;
|
|
}
|
|
|
|
@@ -4102,13 +4121,15 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
*/
|
|
if (!ieee80211_is_s1g_beacon(hdr->frame_control))
|
|
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
|
|
- ncrc = ieee802_11_parse_elems_crc(variable,
|
|
- len - baselen, false, &elems,
|
|
- care_about_ies, ncrc,
|
|
- mgmt->bssid, bssid);
|
|
+ elems = ieee802_11_parse_elems_crc(variable, len - baselen,
|
|
+ false, care_about_ies, ncrc,
|
|
+ mgmt->bssid, bssid);
|
|
+ if (!elems)
|
|
+ return;
|
|
+ ncrc = elems->crc;
|
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
|
|
- ieee80211_check_tim(elems.tim, elems.tim_len, bss_conf->aid)) {
|
|
+ ieee80211_check_tim(elems->tim, elems->tim_len, bss_conf->aid)) {
|
|
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
@@ -4178,12 +4199,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
rx_status->device_timestamp;
|
|
- sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
|
|
+ sdata->vif.bss_conf.sync_dtim_count = elems->dtim_count;
|
|
}
|
|
|
|
if ((ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) ||
|
|
ieee80211_is_s1g_short_beacon(mgmt->frame_control))
|
|
- return;
|
|
+ goto free;
|
|
ifmgd->beacon_crc = ncrc;
|
|
ifmgd->beacon_crc_valid = true;
|
|
|
|
@@ -4191,12 +4212,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
|
|
rx_status->device_timestamp,
|
|
- &elems, true);
|
|
+ elems, true);
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
|
|
- ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
|
|
- elems.wmm_param_len,
|
|
- elems.mu_edca_param_set))
|
|
+ ieee80211_sta_wmm_params(local, sdata, elems->wmm_param,
|
|
+ elems->wmm_param_len,
|
|
+ elems->mu_edca_param_set))
|
|
changed |= BSS_CHANGED_QOS;
|
|
|
|
/*
|
|
@@ -4205,7 +4226,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
*/
|
|
if (!ifmgd->have_beacon) {
|
|
/* a few bogus AP send dtim_period = 0 or no TIM IE */
|
|
- bss_conf->dtim_period = elems.dtim_period ?: 1;
|
|
+ bss_conf->dtim_period = elems->dtim_period ?: 1;
|
|
|
|
changed |= BSS_CHANGED_BEACON_INFO;
|
|
ifmgd->have_beacon = true;
|
|
@@ -4217,9 +4238,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
}
|
|
|
|
- if (elems.erp_info) {
|
|
+ if (elems->erp_info) {
|
|
erp_valid = true;
|
|
- erp_value = elems.erp_info[0];
|
|
+ erp_value = elems->erp_info[0];
|
|
} else {
|
|
erp_valid = false;
|
|
}
|
|
@@ -4232,12 +4253,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
mutex_lock(&local->sta_mtx);
|
|
sta = sta_info_get(sdata, bssid);
|
|
|
|
- changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
|
|
+ changed |= ieee80211_recalc_twt_req(sdata, sta, elems);
|
|
|
|
- if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
|
|
- elems.vht_cap_elem, elems.ht_operation,
|
|
- elems.vht_operation, elems.he_operation,
|
|
- elems.s1g_oper, bssid, &changed)) {
|
|
+ if (ieee80211_config_bw(sdata, sta, elems->ht_cap_elem,
|
|
+ elems->vht_cap_elem, elems->ht_operation,
|
|
+ elems->vht_operation, elems->he_operation,
|
|
+ elems->s1g_oper, bssid, &changed)) {
|
|
mutex_unlock(&local->sta_mtx);
|
|
sdata_info(sdata,
|
|
"failed to follow AP %pM bandwidth change, disconnect\n",
|
|
@@ -4249,21 +4270,23 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
sizeof(deauth_buf), true,
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
false);
|
|
- return;
|
|
+ goto free;
|
|
}
|
|
|
|
- if (sta && elems.opmode_notif)
|
|
- ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
|
|
+ if (sta && elems->opmode_notif)
|
|
+ ieee80211_vht_handle_opmode(sdata, sta, *elems->opmode_notif,
|
|
rx_status->band);
|
|
mutex_unlock(&local->sta_mtx);
|
|
|
|
changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
|
|
- elems.country_elem,
|
|
- elems.country_elem_len,
|
|
- elems.pwr_constr_elem,
|
|
- elems.cisco_dtpc_elem);
|
|
+ elems->country_elem,
|
|
+ elems->country_elem_len,
|
|
+ elems->pwr_constr_elem,
|
|
+ elems->cisco_dtpc_elem);
|
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
+free:
|
|
+ kfree(elems);
|
|
}
|
|
|
|
void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
|
|
@@ -4292,7 +4315,6 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
struct ieee80211_rx_status *rx_status;
|
|
struct ieee80211_mgmt *mgmt;
|
|
u16 fc;
|
|
- struct ieee802_11_elems elems;
|
|
int ies_len;
|
|
|
|
rx_status = (struct ieee80211_rx_status *) skb->cb;
|
|
@@ -4324,6 +4346,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
break;
|
|
case IEEE80211_STYPE_ACTION:
|
|
if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
|
|
+ struct ieee802_11_elems *elems;
|
|
+
|
|
ies_len = skb->len -
|
|
offsetof(struct ieee80211_mgmt,
|
|
u.action.u.chan_switch.variable);
|
|
@@ -4332,18 +4356,19 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
break;
|
|
|
|
/* CSA IE cannot be overridden, no need for BSSID */
|
|
- ieee802_11_parse_elems(
|
|
- mgmt->u.action.u.chan_switch.variable,
|
|
- ies_len, true, &elems, mgmt->bssid, NULL);
|
|
-
|
|
- if (elems.parse_error)
|
|
- break;
|
|
-
|
|
- ieee80211_sta_process_chanswitch(sdata,
|
|
- rx_status->mactime,
|
|
- rx_status->device_timestamp,
|
|
- &elems, false);
|
|
+ elems = ieee802_11_parse_elems(
|
|
+ mgmt->u.action.u.chan_switch.variable,
|
|
+ ies_len, true, mgmt->bssid, NULL);
|
|
+
|
|
+ if (elems && !elems->parse_error)
|
|
+ ieee80211_sta_process_chanswitch(sdata,
|
|
+ rx_status->mactime,
|
|
+ rx_status->device_timestamp,
|
|
+ elems, false);
|
|
+ kfree(elems);
|
|
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
|
|
+ struct ieee802_11_elems *elems;
|
|
+
|
|
ies_len = skb->len -
|
|
offsetof(struct ieee80211_mgmt,
|
|
u.action.u.ext_chan_switch.variable);
|
|
@@ -4355,21 +4380,22 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
* extended CSA IE can't be overridden, no need for
|
|
* BSSID
|
|
*/
|
|
- ieee802_11_parse_elems(
|
|
- mgmt->u.action.u.ext_chan_switch.variable,
|
|
- ies_len, true, &elems, mgmt->bssid, NULL);
|
|
-
|
|
- if (elems.parse_error)
|
|
- break;
|
|
-
|
|
- /* for the handling code pretend this was also an IE */
|
|
- elems.ext_chansw_ie =
|
|
- &mgmt->u.action.u.ext_chan_switch.data;
|
|
+ elems = ieee802_11_parse_elems(
|
|
+ mgmt->u.action.u.ext_chan_switch.variable,
|
|
+ ies_len, true, mgmt->bssid, NULL);
|
|
+
|
|
+ if (elems && !elems->parse_error) {
|
|
+ /* for the handling code pretend it was an IE */
|
|
+ elems->ext_chansw_ie =
|
|
+ &mgmt->u.action.u.ext_chan_switch.data;
|
|
+
|
|
+ ieee80211_sta_process_chanswitch(sdata,
|
|
+ rx_status->mactime,
|
|
+ rx_status->device_timestamp,
|
|
+ elems, false);
|
|
+ }
|
|
|
|
- ieee80211_sta_process_chanswitch(sdata,
|
|
- rx_status->mactime,
|
|
- rx_status->device_timestamp,
|
|
- &elems, false);
|
|
+ kfree(elems);
|
|
}
|
|
break;
|
|
}
|
|
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
|
|
index 0e6133c..61ff2ff 100644
|
|
--- a/net/mac80211/rc80211_minstrel_ht.c
|
|
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
|
@@ -514,6 +514,14 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
|
|
int cur_tp_avg, cur_group, cur_idx;
|
|
int max_gpr_group, max_gpr_idx;
|
|
int max_gpr_tp_avg, max_gpr_prob;
|
|
+ int min_dur;
|
|
+
|
|
+ min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
|
|
+ minstrel_get_duration(mi->max_tp_rate[1]));
|
|
+
|
|
+ /* make the rate at least 18% slower than max tp rates */
|
|
+ if (minstrel_get_duration(index) <= min_dur * 19 / 16)
|
|
+ return;
|
|
|
|
cur_group = MI_RATE_GROUP(index);
|
|
cur_idx = MI_RATE_IDX(index);
|
|
@@ -535,11 +543,6 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
|
|
!minstrel_ht_is_legacy_group(max_tp_group))
|
|
return;
|
|
|
|
- /* skip rates faster than max tp rate with lower prob */
|
|
- if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
|
|
- mrs->prob_avg < max_tp_prob)
|
|
- return;
|
|
-
|
|
max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
|
|
max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
|
|
max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
|
|
@@ -597,40 +600,6 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
|
|
|
|
}
|
|
|
|
-/*
|
|
- * Try to increase robustness of max_prob rate by decrease number of
|
|
- * streams if possible.
|
|
- */
|
|
-static inline void
|
|
-minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
|
|
-{
|
|
- struct minstrel_mcs_group_data *mg;
|
|
- int tmp_max_streams, group, tmp_idx, tmp_prob;
|
|
- int tmp_tp = 0;
|
|
-
|
|
- if (!mi->sta->ht_cap.ht_supported)
|
|
- return;
|
|
-
|
|
- group = MI_RATE_GROUP(mi->max_tp_rate[0]);
|
|
- tmp_max_streams = minstrel_mcs_groups[group].streams;
|
|
- for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
|
|
- mg = &mi->groups[group];
|
|
- if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
|
|
- continue;
|
|
-
|
|
- tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
|
|
- tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
|
|
-
|
|
- if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
|
|
- (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
|
|
- mi->max_prob_rate = mg->max_group_prob_rate;
|
|
- tmp_tp = minstrel_ht_get_tp_avg(mi, group,
|
|
- tmp_idx,
|
|
- tmp_prob);
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
static u16
|
|
__minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
|
|
enum minstrel_sample_type type)
|
|
@@ -703,7 +672,8 @@ minstrel_ht_calc_rate_stats(struct minstrel_priv *mp,
|
|
unsigned int cur_prob;
|
|
|
|
if (unlikely(mrs->attempts > 0)) {
|
|
- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
|
|
+ cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
|
|
+ mrs->attempts + mrs->last_attempts);
|
|
minstrel_filter_avg_add(&mrs->prob_avg,
|
|
&mrs->prob_avg_1, cur_prob);
|
|
mrs->att_hist += mrs->attempts;
|
|
@@ -1109,8 +1079,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|
|
|
mi->max_prob_rate = tmp_max_prob_rate;
|
|
|
|
- /* Try to increase robustness of max_prob_rate*/
|
|
- minstrel_ht_prob_rate_reduce_streams(mi);
|
|
minstrel_ht_refill_sample_rates(mi);
|
|
|
|
#ifdef CPTCFG_MAC80211_DEBUGFS
|
|
@@ -1155,7 +1123,7 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
|
}
|
|
|
|
static void
|
|
-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
|
|
+minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
|
|
{
|
|
int group, orig_group;
|
|
|
|
@@ -1170,11 +1138,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
|
|
minstrel_mcs_groups[orig_group].streams)
|
|
continue;
|
|
|
|
- if (primary)
|
|
- *idx = mi->groups[group].max_group_tp_rate[0];
|
|
- else
|
|
- *idx = mi->groups[group].max_group_tp_rate[1];
|
|
- break;
|
|
+ *idx = mi->groups[group].max_group_prob_rate;
|
|
}
|
|
}
|
|
|
|
@@ -1185,7 +1149,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|
struct ieee80211_tx_info *info = st->info;
|
|
struct minstrel_ht_sta *mi = priv_sta;
|
|
struct ieee80211_tx_rate *ar = info->status.rates;
|
|
- struct minstrel_rate_stats *rate, *rate2;
|
|
+ struct minstrel_rate_stats *rate;
|
|
struct minstrel_priv *mp = priv;
|
|
u32 update_interval = mp->update_interval;
|
|
bool last, update = false;
|
|
@@ -1235,18 +1199,13 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|
/*
|
|
* check for sudden death of spatial multiplexing,
|
|
* downgrade to a lower number of streams if necessary.
|
|
+ * only do this for the max_prob_rate to prevent spurious
|
|
+ * rate fluctuations when the link changes suddenly
|
|
*/
|
|
- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
|
+ rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
|
|
if (rate->attempts > 30 &&
|
|
rate->success < rate->attempts / 4) {
|
|
- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
|
- update = true;
|
|
- }
|
|
-
|
|
- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
|
- if (rate2->attempts > 30 &&
|
|
- rate2->success < rate2->attempts / 4) {
|
|
- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
|
+ minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
|
|
update = true;
|
|
}
|
|
}
|
|
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
|
|
index a5b56e5..dad1403 100644
|
|
--- a/net/mac80211/rc80211_minstrel_ht.h
|
|
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
|
@@ -14,7 +14,7 @@
|
|
|
|
/* scaled fraction values */
|
|
#define MINSTREL_SCALE 12
|
|
-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
|
|
+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
|
|
#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
|
|
|
|
#define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */
|
|
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
|
|
index 2f34d3d..91bfceb 100644
|
|
--- a/net/mac80211/rx.c
|
|
+++ b/net/mac80211/rx.c
|
|
@@ -1583,8 +1583,12 @@ 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);
|
|
|
|
- ieee80211_unschedule_txq(&local->hw, txq, false);
|
|
+ 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]);
|
|
|
|
if (txq_has_queue(txq))
|
|
set_bit(tid, &sta->txq_buffered_tids);
|
|
@@ -1982,10 +1986,11 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
|
|
|
|
if (mmie_keyidx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS ||
|
|
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS +
|
|
- NUM_DEFAULT_BEACON_KEYS) {
|
|
- cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
|
|
- skb->data,
|
|
- skb->len);
|
|
+ NUM_DEFAULT_BEACON_KEYS) {
|
|
+ if (rx->sdata->dev)
|
|
+ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
|
|
+ skb->data,
|
|
+ skb->len);
|
|
return RX_DROP_MONITOR; /* unexpected BIP keyidx */
|
|
}
|
|
|
|
@@ -2133,7 +2138,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
|
|
/* either the frame has been decrypted or will be dropped */
|
|
status->flag |= RX_FLAG_DECRYPTED;
|
|
|
|
- if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE))
|
|
+ if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE &&
|
|
+ rx->sdata->dev))
|
|
cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
|
|
skb->data, skb->len);
|
|
|
|
@@ -2948,6 +2954,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
|
|
if (!fwd_skb)
|
|
goto out;
|
|
|
|
+ fwd_skb->dev = sdata->dev;
|
|
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
|
|
fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
|
|
info = IEEE80211_SKB_CB(fwd_skb);
|
|
@@ -3175,6 +3182,49 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
|
|
ieee80211_tx_skb(sdata, skb);
|
|
}
|
|
|
|
+static void
|
|
+ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
|
|
+{
|
|
+ struct ieee80211_mgmt *mgmt = (void *)rx->skb->data;
|
|
+ const struct element *ie;
|
|
+ size_t baselen;
|
|
+
|
|
+ if (!wiphy_ext_feature_isset(rx->local->hw.wiphy,
|
|
+ NL80211_EXT_FEATURE_BSS_COLOR))
|
|
+ return;
|
|
+
|
|
+ if (ieee80211_hw_check(&rx->local->hw, DETECTS_COLOR_COLLISION))
|
|
+ return;
|
|
+
|
|
+ if (rx->sdata->vif.csa_active)
|
|
+ return;
|
|
+
|
|
+ baselen = mgmt->u.beacon.variable - rx->skb->data;
|
|
+ if (baselen > rx->skb->len)
|
|
+ return;
|
|
+
|
|
+ ie = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION,
|
|
+ mgmt->u.beacon.variable,
|
|
+ rx->skb->len - baselen);
|
|
+ if (ie && ie->datalen >= sizeof(struct ieee80211_he_operation) &&
|
|
+ ie->datalen >= ieee80211_he_oper_size(ie->data + 1)) {
|
|
+ struct ieee80211_bss_conf *bss_conf = &rx->sdata->vif.bss_conf;
|
|
+ const struct ieee80211_he_operation *he_oper;
|
|
+ u8 color;
|
|
+
|
|
+ he_oper = (void *)(ie->data + 1);
|
|
+ if (le32_get_bits(he_oper->he_oper_params,
|
|
+ IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED))
|
|
+ return;
|
|
+
|
|
+ color = le32_get_bits(he_oper->he_oper_params,
|
|
+ IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
|
|
+ if (color == bss_conf->he_bss_color.color)
|
|
+ ieeee80211_obss_color_collision_notify(&rx->sdata->vif,
|
|
+ BIT_ULL(color));
|
|
+ }
|
|
+}
|
|
+
|
|
static ieee80211_rx_result debug_noinline
|
|
ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
|
|
{
|
|
@@ -3200,6 +3250,9 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
|
|
!(rx->flags & IEEE80211_RX_BEACON_REPORTED)) {
|
|
int sig = 0;
|
|
|
|
+ /* sw bss color collision detection */
|
|
+ ieee80211_rx_check_bss_color_collision(rx);
|
|
+
|
|
if (ieee80211_hw_check(&rx->local->hw, SIGNAL_DBM) &&
|
|
!(status->flag & RX_FLAG_NO_SIGNAL_VAL))
|
|
sig = status->signal;
|
|
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
|
|
index 887f945..e692a24 100644
|
|
--- a/net/mac80211/scan.c
|
|
+++ b/net/mac80211/scan.c
|
|
@@ -9,7 +9,7 @@
|
|
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
|
|
* Copyright 2013-2015 Intel Mobile Communications GmbH
|
|
* Copyright 2016-2017 Intel Deutschland GmbH
|
|
- * Copyright (C) 2018-2020 Intel Corporation
|
|
+ * Copyright (C) 2018-2021 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/if_arp.h>
|
|
@@ -155,7 +155,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
|
|
};
|
|
bool signal_valid;
|
|
struct ieee80211_sub_if_data *scan_sdata;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
size_t baselen;
|
|
u8 *elements;
|
|
|
|
@@ -209,8 +209,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
|
|
if (baselen > len)
|
|
return NULL;
|
|
|
|
- ieee802_11_parse_elems(elements, len - baselen, false, &elems,
|
|
- mgmt->bssid, cbss->bssid);
|
|
+ elems = ieee802_11_parse_elems(elements, len - baselen, false,
|
|
+ mgmt->bssid, cbss->bssid);
|
|
+ if (!elems)
|
|
+ return NULL;
|
|
|
|
/* In case the signal is invalid update the status */
|
|
signal_valid = channel == cbss->channel;
|
|
@@ -218,15 +220,17 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
|
|
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
|
|
|
|
bss = (void *)cbss->priv;
|
|
- ieee80211_update_bss_from_elems(local, bss, &elems, rx_status, beacon);
|
|
+ ieee80211_update_bss_from_elems(local, bss, elems, rx_status, beacon);
|
|
|
|
list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) {
|
|
non_tx_bss = (void *)non_tx_cbss->priv;
|
|
|
|
- ieee80211_update_bss_from_elems(local, non_tx_bss, &elems,
|
|
+ ieee80211_update_bss_from_elems(local, non_tx_bss, elems,
|
|
rx_status, beacon);
|
|
}
|
|
|
|
+ kfree(elems);
|
|
+
|
|
return bss;
|
|
}
|
|
|
|
@@ -461,16 +465,19 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
|
scan_req = rcu_dereference_protected(local->scan_req,
|
|
lockdep_is_held(&local->mtx));
|
|
|
|
- if (scan_req != local->int_scan_req) {
|
|
- local->scan_info.aborted = aborted;
|
|
- cfg80211_scan_done(scan_req, &local->scan_info);
|
|
- }
|
|
RCU_INIT_POINTER(local->scan_req, NULL);
|
|
RCU_INIT_POINTER(local->scan_sdata, NULL);
|
|
|
|
local->scanning = 0;
|
|
local->scan_chandef.chan = NULL;
|
|
|
|
+ synchronize_rcu();
|
|
+
|
|
+ if (scan_req != local->int_scan_req) {
|
|
+ local->scan_info.aborted = aborted;
|
|
+ cfg80211_scan_done(scan_req, &local->scan_info);
|
|
+ }
|
|
+
|
|
/* Set power back to normal operating levels. */
|
|
ieee80211_hw_config(local, 0);
|
|
|
|
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
|
|
index dccaf4b..d67dfdc 100644
|
|
--- a/net/mac80211/sta_info.c
|
|
+++ b/net/mac80211/sta_info.c
|
|
@@ -357,6 +357,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
|
|
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
|
mutex_init(&sta->ampdu_mlme.mtx);
|
|
+ sta->ampdu_mlme.dialog_token_allocator = prandom_u32_max(U8_MAX);
|
|
#ifdef CPTCFG_MAC80211_MESH
|
|
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
|
sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
|
|
@@ -425,11 +426,15 @@ 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]);
|
|
- init_airtime_info(&sta->airtime[i], &local->airtime[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];
|
|
}
|
|
|
|
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
|
|
@@ -1888,59 +1893,29 @@ 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)
|
|
+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
|
|
+ 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;
|
|
+ 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;
|
|
+ u32 diff;
|
|
|
|
- air_sched = &local->airtime[txq->ac];
|
|
- air_info = to_airtime_info(txq);
|
|
-
|
|
- if (local->airtime_flags & AIRTIME_USE_TX)
|
|
+ if (sta->local->airtime_flags & AIRTIME_USE_TX)
|
|
airtime += tx_airtime;
|
|
- if (local->airtime_flags & AIRTIME_USE_RX)
|
|
+ if (sta->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;
|
|
- }
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
+ sta->airtime[ac].tx_airtime += tx_airtime;
|
|
+ sta->airtime[ac].rx_airtime += rx_airtime;
|
|
|
|
- /* 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);
|
|
+ diff = (u32)jiffies - sta->airtime[ac].last_active;
|
|
+ if (diff <= AIRTIME_ACTIVE_DURATION)
|
|
+ sta->airtime[ac].deficit -= airtime;
|
|
|
|
- spin_unlock_bh(&air_sched->lock);
|
|
-}
|
|
-
|
|
-void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
|
|
- u32 tx_airtime, u32 rx_airtime)
|
|
-{
|
|
- struct ieee80211_txq *txq = pubsta->txq[tid];
|
|
-
|
|
- if (!txq)
|
|
- return;
|
|
-
|
|
- ieee80211_register_airtime(txq, tx_airtime, rx_airtime);
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_sta_register_airtime);
|
|
|
|
@@ -1959,6 +1934,7 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
|
|
&sta->airtime[ac].aql_tx_pending);
|
|
|
|
atomic_add(tx_airtime, &local->aql_total_pending_airtime);
|
|
+ atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]);
|
|
return;
|
|
}
|
|
|
|
@@ -1970,14 +1946,17 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
|
|
tx_pending, 0);
|
|
}
|
|
|
|
+ atomic_sub(tx_airtime, &local->aql_total_pending_airtime);
|
|
tx_pending = atomic_sub_return(tx_airtime,
|
|
- &local->aql_total_pending_airtime);
|
|
+ &local->aql_ac_pending_airtime[ac]);
|
|
if (WARN_ONCE(tx_pending < 0,
|
|
"Device %s AC %d pending airtime underflow: %u, %u",
|
|
wiphy_name(local->hw.wiphy), ac, tx_pending,
|
|
- tx_airtime))
|
|
- atomic_cmpxchg(&local->aql_total_pending_airtime,
|
|
+ tx_airtime)) {
|
|
+ atomic_cmpxchg(&local->aql_ac_pending_airtime[ac],
|
|
tx_pending, 0);
|
|
+ atomic_sub(tx_pending, &local->aql_total_pending_airtime);
|
|
+ }
|
|
}
|
|
|
|
int sta_info_move_state(struct sta_info *sta,
|
|
@@ -2384,7 +2363,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[0].weight;
|
|
+ sinfo->airtime_weight = sta->airtime_weight;
|
|
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
|
|
}
|
|
|
|
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
|
|
index 78b0c18..70600a6 100644
|
|
--- a/net/mac80211/sta_info.h
|
|
+++ b/net/mac80211/sta_info.h
|
|
@@ -135,25 +135,19 @@ 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;
|
|
- u64 v_t;
|
|
- u64 last_scheduled;
|
|
- struct list_head list;
|
|
+ u32 last_active;
|
|
+ s32 deficit;
|
|
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;
|
|
|
|
@@ -523,6 +517,7 @@ 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
|
|
@@ -653,6 +648,7 @@ 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.
|
|
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
|
|
index ef17d43..97d57cb 100644
|
|
--- a/net/mac80211/status.c
|
|
+++ b/net/mac80211/status.c
|
|
@@ -983,25 +983,6 @@ 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
|
|
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
|
|
index 45e532a..137be9e 100644
|
|
--- a/net/mac80211/tdls.c
|
|
+++ b/net/mac80211/tdls.c
|
|
@@ -6,7 +6,7 @@
|
|
* Copyright 2014, Intel Corporation
|
|
* Copyright 2014 Intel Mobile Communications GmbH
|
|
* Copyright 2015 - 2016 Intel Deutschland GmbH
|
|
- * Copyright (C) 2019 Intel Corporation
|
|
+ * Copyright (C) 2019, 2021 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/ieee80211.h>
|
|
@@ -1684,7 +1684,7 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_local *local = sdata->local;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems = NULL;
|
|
struct sta_info *sta;
|
|
struct ieee80211_tdls_data *tf = (void *)skb->data;
|
|
bool local_initiator;
|
|
@@ -1718,16 +1718,20 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
|
|
goto call_drv;
|
|
}
|
|
|
|
- ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
|
|
- skb->len - baselen, false, &elems,
|
|
- NULL, NULL);
|
|
- if (elems.parse_error) {
|
|
+ elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
|
|
+ skb->len - baselen, false, NULL, NULL);
|
|
+ if (!elems) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (elems->parse_error) {
|
|
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
- if (!elems.ch_sw_timing || !elems.lnk_id) {
|
|
+ if (!elems->ch_sw_timing || !elems->lnk_id) {
|
|
tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
@@ -1735,15 +1739,15 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
|
/* validate the initiator is set correctly */
|
|
local_initiator =
|
|
- !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
|
|
+ !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
|
|
if (local_initiator == sta->sta.tdls_initiator) {
|
|
tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
- params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
|
|
- params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
|
|
+ params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time);
|
|
+ params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout);
|
|
|
|
params.tmpl_skb =
|
|
ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie);
|
|
@@ -1763,6 +1767,7 @@ call_drv:
|
|
out:
|
|
mutex_unlock(&local->sta_mtx);
|
|
dev_kfree_skb_any(params.tmpl_skb);
|
|
+ kfree(elems);
|
|
return ret;
|
|
}
|
|
|
|
@@ -1771,7 +1776,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_local *local = sdata->local;
|
|
- struct ieee802_11_elems elems;
|
|
+ struct ieee802_11_elems *elems;
|
|
struct cfg80211_chan_def chandef;
|
|
struct ieee80211_channel *chan;
|
|
enum nl80211_channel_type chan_type;
|
|
@@ -1831,22 +1836,27 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
|
|
- skb->len - baselen, false, &elems, NULL, NULL);
|
|
- if (elems.parse_error) {
|
|
+ elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
|
|
+ skb->len - baselen, false, NULL, NULL);
|
|
+ if (!elems)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (elems->parse_error) {
|
|
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
|
|
- return -EINVAL;
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
}
|
|
|
|
- if (!elems.ch_sw_timing || !elems.lnk_id) {
|
|
+ if (!elems->ch_sw_timing || !elems->lnk_id) {
|
|
tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n");
|
|
- return -EINVAL;
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
}
|
|
|
|
- if (!elems.sec_chan_offs) {
|
|
+ if (!elems->sec_chan_offs) {
|
|
chan_type = NL80211_CHAN_HT20;
|
|
} else {
|
|
- switch (elems.sec_chan_offs->sec_chan_offs) {
|
|
+ switch (elems->sec_chan_offs->sec_chan_offs) {
|
|
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
chan_type = NL80211_CHAN_HT40PLUS;
|
|
break;
|
|
@@ -1865,7 +1875,8 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
|
if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef,
|
|
sdata->wdev.iftype)) {
|
|
tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n");
|
|
- return -EINVAL;
|
|
+ ret = -EINVAL;
|
|
+ goto free;
|
|
}
|
|
|
|
mutex_lock(&local->sta_mtx);
|
|
@@ -1881,7 +1892,7 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
|
|
|
/* validate the initiator is set correctly */
|
|
local_initiator =
|
|
- !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
|
|
+ !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
|
|
if (local_initiator == sta->sta.tdls_initiator) {
|
|
tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
|
|
ret = -EINVAL;
|
|
@@ -1889,16 +1900,16 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
|
}
|
|
|
|
/* peer should have known better */
|
|
- if (!sta->sta.ht_cap.ht_supported && elems.sec_chan_offs &&
|
|
- elems.sec_chan_offs->sec_chan_offs) {
|
|
+ if (!sta->sta.ht_cap.ht_supported && elems->sec_chan_offs &&
|
|
+ elems->sec_chan_offs->sec_chan_offs) {
|
|
tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
|
|
ret = -ENOTSUPP;
|
|
goto out;
|
|
}
|
|
|
|
params.chandef = &chandef;
|
|
- params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
|
|
- params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
|
|
+ params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time);
|
|
+ params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout);
|
|
|
|
params.tmpl_skb =
|
|
ieee80211_tdls_ch_sw_resp_tmpl_get(sta,
|
|
@@ -1917,6 +1928,8 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
|
out:
|
|
mutex_unlock(&local->sta_mtx);
|
|
dev_kfree_skb_any(params.tmpl_skb);
|
|
+free:
|
|
+ kfree(elems);
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
|
|
index 9e8381b..ffa1b7b 100644
|
|
--- a/net/mac80211/trace.h
|
|
+++ b/net/mac80211/trace.h
|
|
@@ -2892,6 +2892,15 @@ TRACE_EVENT(drv_twt_teardown_request,
|
|
)
|
|
);
|
|
|
|
+#if LINUX_VERSION_IS_GEQ(5,13,0)
|
|
+DEFINE_EVENT(sta_event, drv_net_fill_forward_path,
|
|
+ TP_PROTO(struct ieee80211_local *local,
|
|
+ struct ieee80211_sub_if_data *sdata,
|
|
+ struct ieee80211_sta *sta),
|
|
+ TP_ARGS(local, sdata, sta)
|
|
+);
|
|
+#endif
|
|
+
|
|
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
|
|
|
|
#undef TRACE_INCLUDE_PATH
|
|
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
|
|
index 15888e4..50025ff 100644
|
|
--- a/net/mac80211/tx.c
|
|
+++ b/net/mac80211/tx.c
|
|
@@ -18,7 +18,6 @@
|
|
#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>
|
|
@@ -1480,7 +1479,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);
|
|
- RB_CLEAR_NODE(&txqi->schedule_order);
|
|
+ INIT_LIST_HEAD(&txqi->schedule_order);
|
|
|
|
txqi->txq.vif = &sdata->vif;
|
|
|
|
@@ -1524,7 +1523,9 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
|
|
ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
|
|
spin_unlock_bh(&fq->lock);
|
|
|
|
- ieee80211_unschedule_txq(&local->hw, &txqi->txq, true);
|
|
+ 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]);
|
|
}
|
|
|
|
void ieee80211_txq_set_params(struct ieee80211_local *local)
|
|
@@ -3791,7 +3792,7 @@ begin:
|
|
encap_out:
|
|
IEEE80211_SKB_CB(skb)->control.vif = vif;
|
|
|
|
- if (vif &&
|
|
+ if (tx.sta &&
|
|
wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
|
|
bool ampdu = txq->ac != IEEE80211_AC_VO;
|
|
u32 airtime;
|
|
@@ -3816,262 +3817,147 @@ out:
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_tx_dequeue);
|
|
|
|
-struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
|
+static inline s32 ieee80211_sta_deficit(struct sta_info *sta, 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 airtime_info *air_info;
|
|
- struct txq_info *txqi = NULL;
|
|
- struct rb_node *node;
|
|
- bool first = false;
|
|
-
|
|
- air_sched = &local->airtime[ac];
|
|
- spin_lock_bh(&air_sched->lock);
|
|
-
|
|
- node = air_sched->schedule_pos;
|
|
-
|
|
-begin:
|
|
- if (!node) {
|
|
- node = rb_first_cached(&air_sched->active_txqs);
|
|
- first = true;
|
|
- } else {
|
|
- node = rb_next(node);
|
|
- }
|
|
-
|
|
- if (!node)
|
|
- goto out;
|
|
-
|
|
- 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;
|
|
- }
|
|
+ struct airtime_info *air_info = &sta->airtime[ac];
|
|
|
|
- air_sched->schedule_pos = node;
|
|
- air_sched->last_schedule_activity = now;
|
|
- ret = &txqi->txq;
|
|
-out:
|
|
- spin_unlock_bh(&air_sched->lock);
|
|
- return ret;
|
|
-}
|
|
-EXPORT_SYMBOL(ieee80211_next_txq);
|
|
-
|
|
-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);
|
|
+ return air_info->deficit - atomic_read(&air_info->aql_tx_pending);
|
|
}
|
|
|
|
-void ieee80211_resort_txq(struct ieee80211_hw *hw,
|
|
- struct ieee80211_txq *txq)
|
|
+static void
|
|
+ieee80211_txq_set_active(struct txq_info *txqi)
|
|
{
|
|
- 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;
|
|
+ struct sta_info *sta;
|
|
|
|
- if (air_sched->schedule_pos == &txqi->schedule_order)
|
|
- air_sched->schedule_pos = n_prev;
|
|
+ if (!txqi->txq.sta)
|
|
+ return;
|
|
|
|
- rb_erase_cached(&txqi->schedule_order,
|
|
- &air_sched->active_txqs);
|
|
- RB_CLEAR_NODE(&txqi->schedule_order);
|
|
- __ieee80211_insert_txq(&air_sched->active_txqs, txqi);
|
|
- }
|
|
+ sta = container_of(txqi->txq.sta, struct sta_info, sta);
|
|
+ sta->airtime[txqi->txq.ac].last_active = (u32)jiffies;
|
|
}
|
|
|
|
-void ieee80211_update_airtime_weight(struct ieee80211_local *local,
|
|
- struct airtime_sched_info *air_sched,
|
|
- u64 now, bool force)
|
|
+static bool
|
|
+ieee80211_txq_keep_active(struct txq_info *txqi)
|
|
{
|
|
- struct airtime_info *air_info, *tmp;
|
|
- u64 weight_sum = 0;
|
|
+ struct sta_info *sta;
|
|
+ u32 diff;
|
|
|
|
- if (unlikely(!now))
|
|
- now = ktime_get_boottime_ns();
|
|
+ if (!txqi->txq.sta)
|
|
+ return false;
|
|
|
|
- lockdep_assert_held(&air_sched->lock);
|
|
+ sta = container_of(txqi->txq.sta, struct sta_info, sta);
|
|
+ if (ieee80211_sta_deficit(sta, txqi->txq.ac) >= 0)
|
|
+ return false;
|
|
|
|
- if (!force && (air_sched->last_weight_update <
|
|
- now - AIRTIME_ACTIVE_DURATION))
|
|
- return;
|
|
+ diff = (u32)jiffies - sta->airtime[txqi->txq.ac].last_active;
|
|
|
|
- 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;
|
|
+ return diff <= AIRTIME_ACTIVE_DURATION;
|
|
}
|
|
|
|
-void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
|
- struct ieee80211_txq *txq)
|
|
- __acquires(txq_lock) __releases(txq_lock)
|
|
+struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
|
{
|
|
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);
|
|
+ struct ieee80211_txq *ret = NULL;
|
|
+ struct txq_info *txqi = NULL, *head = NULL;
|
|
+ bool found_eligible_txq = false;
|
|
|
|
- spin_lock_bh(&air_sched->lock);
|
|
- was_active = airtime_is_active(air_info, now);
|
|
- airtime_set_active(air_sched, air_info, now);
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
|
|
- if (!RB_EMPTY_NODE(&txqi->schedule_order))
|
|
+ if (!local->schedule_round[ac])
|
|
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;
|
|
+ begin:
|
|
+ txqi = list_first_entry_or_null(&local->active_txqs[ac],
|
|
+ struct txq_info,
|
|
+ schedule_order);
|
|
+ if (!txqi)
|
|
+ goto out;
|
|
|
|
- ieee80211_update_airtime_weight(local, air_sched, now, !was_active);
|
|
- __ieee80211_insert_txq(&air_sched->active_txqs, txqi);
|
|
+ if (txqi == head) {
|
|
+ if (!found_eligible_txq)
|
|
+ goto out;
|
|
+ else
|
|
+ found_eligible_txq = false;
|
|
+ }
|
|
|
|
-out:
|
|
- spin_unlock_bh(&air_sched->lock);
|
|
-}
|
|
-EXPORT_SYMBOL(ieee80211_schedule_txq);
|
|
+ if (!head)
|
|
+ head = txqi;
|
|
|
|
-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;
|
|
+ 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);
|
|
+ s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
|
|
|
|
- air_sched = &local->airtime[txq->ac];
|
|
- air_info = to_airtime_info(&txqi->txq);
|
|
+ if (aql_check)
|
|
+ found_eligible_txq = true;
|
|
|
|
- lockdep_assert_held(&air_sched->lock);
|
|
+ if (deficit < 0)
|
|
+ sta->airtime[txqi->txq.ac].deficit +=
|
|
+ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
|
|
|
|
- if (purge) {
|
|
- list_del_init(&air_info->list);
|
|
- ieee80211_update_airtime_weight(local, air_sched, 0, true);
|
|
+ if (deficit < 0 || !aql_check) {
|
|
+ list_move_tail(&txqi->schedule_order,
|
|
+ &local->active_txqs[txqi->txq.ac]);
|
|
+ goto begin;
|
|
+ }
|
|
}
|
|
|
|
- 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 (txqi->schedule_round == local->schedule_round[ac])
|
|
+ goto out;
|
|
|
|
- if (!purge)
|
|
- airtime_set_active(air_sched, air_info,
|
|
- ktime_get_boottime_ns());
|
|
+ list_del_init(&txqi->schedule_order);
|
|
+ txqi->schedule_round = local->schedule_round[ac];
|
|
+ ret = &txqi->txq;
|
|
|
|
- rb_erase_cached(&txqi->schedule_order,
|
|
- &air_sched->active_txqs);
|
|
- RB_CLEAR_NODE(&txqi->schedule_order);
|
|
+out:
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
+ return ret;
|
|
}
|
|
+EXPORT_SYMBOL(ieee80211_next_txq);
|
|
|
|
-void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
|
|
+void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
|
struct ieee80211_txq *txq,
|
|
- 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)
|
|
+ bool force)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct txq_info *txqi = to_txq_info(txq);
|
|
+ bool has_queue;
|
|
+
|
|
+ spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
|
+
|
|
+ has_queue = force || txq_has_queue(txq);
|
|
+ if (list_empty(&txqi->schedule_order) &&
|
|
+ (has_queue || ieee80211_txq_keep_active(txqi))) {
|
|
+ /* 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 && has_queue &&
|
|
+ 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 (has_queue)
|
|
+ ieee80211_txq_set_active(txqi);
|
|
+ }
|
|
|
|
- spin_lock_bh(&local->airtime[txq->ac].lock);
|
|
-
|
|
- if (!RB_EMPTY_NODE(&txqi->schedule_order) && !force &&
|
|
- !txq_has_queue(txq))
|
|
- __ieee80211_unschedule_txq(hw, txq, false);
|
|
-
|
|
- spin_unlock_bh(&local->airtime[txq->ac].lock);
|
|
+ spin_unlock_bh(&local->active_txq_lock[txq->ac]);
|
|
}
|
|
-EXPORT_SYMBOL(ieee80211_return_txq);
|
|
+EXPORT_SYMBOL(__ieee80211_schedule_txq);
|
|
|
|
DEFINE_STATIC_KEY_FALSE(aql_disable);
|
|
|
|
bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
|
|
struct ieee80211_txq *txq)
|
|
{
|
|
- struct airtime_info *air_info = to_airtime_info(txq);
|
|
+ struct sta_info *sta;
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
|
|
if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
|
|
@@ -4086,74 +3972,109 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
|
|
if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
|
|
return true;
|
|
|
|
- if (atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_low)
|
|
+ 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)
|
|
return true;
|
|
|
|
if (atomic_read(&local->aql_total_pending_airtime) <
|
|
local->aql_threshold &&
|
|
- atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_high)
|
|
+ atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
|
|
+ sta->airtime[txq->ac].aql_limit_high)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_txq_airtime_check);
|
|
|
|
+static bool
|
|
+ieee80211_txq_schedule_airtime_check(struct ieee80211_local *local, u8 ac)
|
|
+{
|
|
+ unsigned int num_txq = 0;
|
|
+ struct txq_info *txq;
|
|
+ u32 aql_limit;
|
|
+
|
|
+ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
|
|
+ return true;
|
|
+
|
|
+ list_for_each_entry(txq, &local->active_txqs[ac], schedule_order)
|
|
+ num_txq++;
|
|
+
|
|
+ aql_limit = (num_txq - 1) * local->aql_txq_limit_low[ac] / 2 +
|
|
+ local->aql_txq_limit_high[ac];
|
|
+
|
|
+ return atomic_read(&local->aql_ac_pending_airtime[ac]) < aql_limit;
|
|
+}
|
|
+
|
|
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 airtime_sched_info *air_sched;
|
|
- struct airtime_info *air_info;
|
|
- struct rb_node *node = NULL;
|
|
- bool ret = false;
|
|
- u64 now;
|
|
-
|
|
+ struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
|
|
+ struct sta_info *sta;
|
|
+ u8 ac = txq->ac;
|
|
|
|
- if (!ieee80211_txq_airtime_check(hw, txq))
|
|
- return false;
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
|
|
- air_sched = &local->airtime[txq->ac];
|
|
- spin_lock_bh(&air_sched->lock);
|
|
+ if (!txqi->txq.sta)
|
|
+ goto out;
|
|
|
|
- if (RB_EMPTY_NODE(&txqi->schedule_order))
|
|
+ if (list_empty(&txqi->schedule_order))
|
|
goto out;
|
|
|
|
- now = ktime_get_boottime_ns();
|
|
+ if (!ieee80211_txq_schedule_airtime_check(local, ac))
|
|
+ goto out;
|
|
|
|
- /* 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);
|
|
+ list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
|
|
+ schedule_order) {
|
|
+ if (iter == txqi)
|
|
+ break;
|
|
|
|
- if (air_sched->v_t < air_info->v_t)
|
|
- airtime_catchup_v_t(air_sched, air_info->v_t, now);
|
|
+ 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 (ieee80211_sta_deficit(sta, ac) < 0)
|
|
+ sta->airtime[ac].deficit += sta->airtime_weight <<
|
|
+ AIRTIME_QUANTUM_SHIFT;
|
|
+ list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
|
|
}
|
|
|
|
- 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 = container_of(txqi->txq.sta, struct sta_info, sta);
|
|
+ if (sta->airtime[ac].deficit >= 0)
|
|
+ goto out;
|
|
+
|
|
+ sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
|
|
+ list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
|
|
+ return false;
|
|
out:
|
|
- spin_unlock_bh(&air_sched->lock);
|
|
- return ret;
|
|
+ if (!list_empty(&txqi->schedule_order))
|
|
+ list_del_init(&txqi->schedule_order);
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
+
|
|
+ return true;
|
|
}
|
|
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(&air_sched->lock);
|
|
- air_sched->schedule_pos = NULL;
|
|
- spin_unlock_bh(&air_sched->lock);
|
|
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
|
+
|
|
+ if (ieee80211_txq_schedule_airtime_check(local, ac)) {
|
|
+ local->schedule_round[ac]++;
|
|
+ if (!local->schedule_round[ac])
|
|
+ local->schedule_round[ac]++;
|
|
+ } else {
|
|
+ local->schedule_round[ac] = 0;
|
|
+ }
|
|
+
|
|
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_txq_schedule_start);
|
|
|
|
@@ -4987,6 +4908,135 @@ static int ieee80211_beacon_protect(struct sk_buff *skb,
|
|
return 0;
|
|
}
|
|
|
|
+static void
|
|
+ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_vif *vif,
|
|
+ struct ieee80211_mutable_offsets *offs,
|
|
+ struct beacon_data *beacon,
|
|
+ struct sk_buff *skb,
|
|
+ struct ieee80211_chanctx_conf *chanctx_conf,
|
|
+ u16 csa_off_base)
|
|
+{
|
|
+ struct ieee80211_local *local = hw_to_local(hw);
|
|
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
+ struct ieee80211_tx_info *info;
|
|
+ enum nl80211_band band;
|
|
+ struct ieee80211_tx_rate_control txrc;
|
|
+
|
|
+ /* CSA offsets */
|
|
+ if (offs && beacon) {
|
|
+ u16 i;
|
|
+
|
|
+ for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) {
|
|
+ u16 csa_off = beacon->cntdwn_counter_offsets[i];
|
|
+
|
|
+ if (!csa_off)
|
|
+ continue;
|
|
+
|
|
+ offs->cntdwn_counter_offs[i] = csa_off_base + csa_off;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ band = chanctx_conf->def.chan->band;
|
|
+ info = IEEE80211_SKB_CB(skb);
|
|
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
|
|
+ info->band = band;
|
|
+
|
|
+ memset(&txrc, 0, sizeof(txrc));
|
|
+ txrc.hw = hw;
|
|
+ txrc.sband = local->hw.wiphy->bands[band];
|
|
+ txrc.bss_conf = &sdata->vif.bss_conf;
|
|
+ txrc.skb = skb;
|
|
+ txrc.reported_rate.idx = -1;
|
|
+ if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band])
|
|
+ txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band];
|
|
+ else
|
|
+ txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
|
|
+ txrc.bss = true;
|
|
+ rate_control_get_rate(sdata, NULL, &txrc);
|
|
+
|
|
+ info->control.vif = vif;
|
|
+ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
|
|
+ IEEE80211_TX_CTL_ASSIGN_SEQ |
|
|
+ IEEE80211_TX_CTL_FIRST_FRAGMENT;
|
|
+}
|
|
+
|
|
+static void
|
|
+ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (!beacon->mbssid_ies)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < beacon->mbssid_ies->cnt; i++)
|
|
+ skb_put_data(skb, beacon->mbssid_ies->elem[i].data,
|
|
+ beacon->mbssid_ies->elem[i].len);
|
|
+}
|
|
+
|
|
+static struct sk_buff *
|
|
+ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
|
|
+ struct ieee80211_vif *vif,
|
|
+ struct ieee80211_mutable_offsets *offs,
|
|
+ bool is_template,
|
|
+ struct beacon_data *beacon,
|
|
+ struct ieee80211_chanctx_conf *chanctx_conf)
|
|
+{
|
|
+ struct ieee80211_local *local = hw_to_local(hw);
|
|
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
+ struct ieee80211_if_ap *ap = &sdata->u.ap;
|
|
+ struct sk_buff *skb = NULL;
|
|
+ u16 csa_off_base = 0;
|
|
+ int mbssid_len;
|
|
+
|
|
+ if (beacon->cntdwn_counter_offsets[0]) {
|
|
+ if (!is_template)
|
|
+ ieee80211_beacon_update_cntdwn(vif);
|
|
+
|
|
+ ieee80211_set_beacon_cntdwn(sdata, beacon);
|
|
+ }
|
|
+
|
|
+ /* headroom, head length,
|
|
+ * tail length, maximum TIM length and multiple BSSID length
|
|
+ */
|
|
+ mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies);
|
|
+ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
|
|
+ beacon->tail_len + 256 +
|
|
+ local->hw.extra_beacon_tailroom + mbssid_len);
|
|
+ if (!skb)
|
|
+ return NULL;
|
|
+
|
|
+ skb_reserve(skb, local->tx_headroom);
|
|
+ skb_put_data(skb, beacon->head, beacon->head_len);
|
|
+
|
|
+ ieee80211_beacon_add_tim(sdata, &ap->ps, skb, is_template);
|
|
+
|
|
+ if (offs) {
|
|
+ offs->tim_offset = beacon->head_len;
|
|
+ offs->tim_length = skb->len - beacon->head_len;
|
|
+ offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
|
|
+
|
|
+ if (mbssid_len) {
|
|
+ ieee80211_beacon_add_mbssid(skb, beacon);
|
|
+ offs->mbssid_off = skb->len - mbssid_len;
|
|
+ }
|
|
+
|
|
+ /* for AP the csa offsets are from tail */
|
|
+ csa_off_base = skb->len;
|
|
+ }
|
|
+
|
|
+ if (beacon->tail)
|
|
+ skb_put_data(skb, beacon->tail, beacon->tail_len);
|
|
+
|
|
+ if (ieee80211_beacon_protect(skb, local, sdata) < 0)
|
|
+ return NULL;
|
|
+
|
|
+ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb, chanctx_conf,
|
|
+ csa_off_base);
|
|
+ return skb;
|
|
+}
|
|
+
|
|
static struct sk_buff *
|
|
__ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif,
|
|
@@ -4996,12 +5046,8 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct beacon_data *beacon = NULL;
|
|
struct sk_buff *skb = NULL;
|
|
- struct ieee80211_tx_info *info;
|
|
struct ieee80211_sub_if_data *sdata = NULL;
|
|
- enum nl80211_band band;
|
|
- struct ieee80211_tx_rate_control txrc;
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
- int csa_off_base = 0;
|
|
|
|
rcu_read_lock();
|
|
|
|
@@ -5018,48 +5064,11 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|
struct ieee80211_if_ap *ap = &sdata->u.ap;
|
|
|
|
beacon = rcu_dereference(ap->beacon);
|
|
- if (beacon) {
|
|
- if (beacon->cntdwn_counter_offsets[0]) {
|
|
- if (!is_template)
|
|
- ieee80211_beacon_update_cntdwn(vif);
|
|
-
|
|
- ieee80211_set_beacon_cntdwn(sdata, beacon);
|
|
- }
|
|
-
|
|
- /*
|
|
- * headroom, head length,
|
|
- * tail length and maximum TIM length
|
|
- */
|
|
- skb = dev_alloc_skb(local->tx_headroom +
|
|
- beacon->head_len +
|
|
- beacon->tail_len + 256 +
|
|
- local->hw.extra_beacon_tailroom);
|
|
- if (!skb)
|
|
- goto out;
|
|
-
|
|
- skb_reserve(skb, local->tx_headroom);
|
|
- skb_put_data(skb, beacon->head, beacon->head_len);
|
|
-
|
|
- ieee80211_beacon_add_tim(sdata, &ap->ps, skb,
|
|
- is_template);
|
|
-
|
|
- if (offs) {
|
|
- offs->tim_offset = beacon->head_len;
|
|
- offs->tim_length = skb->len - beacon->head_len;
|
|
- offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
|
|
-
|
|
- /* for AP the csa offsets are from tail */
|
|
- csa_off_base = skb->len;
|
|
- }
|
|
-
|
|
- if (beacon->tail)
|
|
- skb_put_data(skb, beacon->tail,
|
|
- beacon->tail_len);
|
|
-
|
|
- if (ieee80211_beacon_protect(skb, local, sdata) < 0)
|
|
- goto out;
|
|
- } else
|
|
+ if (!beacon)
|
|
goto out;
|
|
+
|
|
+ skb = ieee80211_beacon_get_ap(hw, vif, offs, is_template,
|
|
+ beacon, chanctx_conf);
|
|
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
|
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
|
struct ieee80211_hdr *hdr;
|
|
@@ -5085,6 +5094,9 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|
hdr = (struct ieee80211_hdr *) skb->data;
|
|
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
IEEE80211_STYPE_BEACON);
|
|
+
|
|
+ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb,
|
|
+ chanctx_conf, 0);
|
|
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
|
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
|
|
|
@@ -5124,51 +5136,13 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|
}
|
|
|
|
skb_put_data(skb, beacon->tail, beacon->tail_len);
|
|
+ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb,
|
|
+ chanctx_conf, 0);
|
|
} else {
|
|
WARN_ON(1);
|
|
goto out;
|
|
}
|
|
|
|
- /* CSA offsets */
|
|
- if (offs && beacon) {
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) {
|
|
- u16 csa_off = beacon->cntdwn_counter_offsets[i];
|
|
-
|
|
- if (!csa_off)
|
|
- continue;
|
|
-
|
|
- offs->cntdwn_counter_offs[i] = csa_off_base + csa_off;
|
|
- }
|
|
- }
|
|
-
|
|
- band = chanctx_conf->def.chan->band;
|
|
-
|
|
- info = IEEE80211_SKB_CB(skb);
|
|
-
|
|
- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
- info->flags |= IEEE80211_TX_CTL_NO_ACK;
|
|
- info->band = band;
|
|
-
|
|
- memset(&txrc, 0, sizeof(txrc));
|
|
- txrc.hw = hw;
|
|
- txrc.sband = local->hw.wiphy->bands[band];
|
|
- txrc.bss_conf = &sdata->vif.bss_conf;
|
|
- txrc.skb = skb;
|
|
- txrc.reported_rate.idx = -1;
|
|
- if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band])
|
|
- txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band];
|
|
- else
|
|
- txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
|
|
- txrc.bss = true;
|
|
- rate_control_get_rate(sdata, NULL, &txrc);
|
|
-
|
|
- info->control.vif = vif;
|
|
-
|
|
- info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
|
|
- IEEE80211_TX_CTL_ASSIGN_SEQ |
|
|
- IEEE80211_TX_CTL_FIRST_FRAGMENT;
|
|
out:
|
|
rcu_read_unlock();
|
|
return skb;
|
|
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
|
|
index be1911d..09a19e6 100644
|
|
--- a/net/mac80211/util.c
|
|
+++ b/net/mac80211/util.c
|
|
@@ -301,6 +301,9 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
|
|
local_bh_disable();
|
|
spin_lock(&fq->lock);
|
|
|
|
+ if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
|
|
+ goto out;
|
|
+
|
|
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
|
ps = &sdata->bss->ps;
|
|
|
|
@@ -1117,10 +1120,6 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
|
} else
|
|
elem_parse_failed = true;
|
|
break;
|
|
- case WLAN_EID_CHALLENGE:
|
|
- elems->challenge = pos;
|
|
- elems->challenge_len = elen;
|
|
- break;
|
|
case WLAN_EID_VENDOR_SPECIFIC:
|
|
if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
|
|
pos[2] == 0xf2) {
|
|
@@ -1400,8 +1399,8 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
|
|
|
static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
|
|
struct ieee802_11_elems *elems,
|
|
- u8 *transmitter_bssid,
|
|
- u8 *bss_bssid,
|
|
+ const u8 *transmitter_bssid,
|
|
+ const u8 *bss_bssid,
|
|
u8 *nontransmitted_profile)
|
|
{
|
|
const struct element *elem, *sub;
|
|
@@ -1414,6 +1413,8 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
|
|
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) {
|
|
if (elem->datalen < 2)
|
|
continue;
|
|
+ if (elem->data[0] < 1 || elem->data[0] > 8)
|
|
+ continue;
|
|
|
|
for_each_element(sub, elem->data + 1, elem->datalen - 1) {
|
|
u8 new_bssid[ETH_ALEN];
|
|
@@ -1466,31 +1467,36 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
|
|
return found ? profile_len : 0;
|
|
}
|
|
|
|
-u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
|
- struct ieee802_11_elems *elems,
|
|
- u64 filter, u32 crc, u8 *transmitter_bssid,
|
|
- u8 *bss_bssid)
|
|
+struct ieee802_11_elems *ieee802_11_parse_elems_crc(const u8 *start, size_t len,
|
|
+ bool action, u64 filter,
|
|
+ u32 crc,
|
|
+ const u8 *transmitter_bssid,
|
|
+ const u8 *bss_bssid)
|
|
{
|
|
+ struct ieee802_11_elems *elems;
|
|
const struct element *non_inherit = NULL;
|
|
u8 *nontransmitted_profile;
|
|
int nontransmitted_profile_len = 0;
|
|
|
|
- memset(elems, 0, sizeof(*elems));
|
|
+ elems = kzalloc(sizeof(*elems) + len, GFP_ATOMIC);
|
|
+ if (!elems)
|
|
+ return NULL;
|
|
elems->ie_start = start;
|
|
elems->total_len = len;
|
|
|
|
- nontransmitted_profile = kmalloc(len, GFP_ATOMIC);
|
|
- if (nontransmitted_profile) {
|
|
- nontransmitted_profile_len =
|
|
- ieee802_11_find_bssid_profile(start, len, elems,
|
|
- transmitter_bssid,
|
|
- bss_bssid,
|
|
- nontransmitted_profile);
|
|
- non_inherit =
|
|
- cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
|
|
- nontransmitted_profile,
|
|
- nontransmitted_profile_len);
|
|
- }
|
|
+ elems->scratch_len = len;
|
|
+ elems->scratch_pos = elems->scratch;
|
|
+
|
|
+ nontransmitted_profile = elems->scratch_pos;
|
|
+ nontransmitted_profile_len =
|
|
+ ieee802_11_find_bssid_profile(start, len, elems,
|
|
+ transmitter_bssid,
|
|
+ bss_bssid,
|
|
+ nontransmitted_profile);
|
|
+ non_inherit =
|
|
+ cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
|
|
+ nontransmitted_profile,
|
|
+ nontransmitted_profile_len);
|
|
|
|
crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter,
|
|
crc, non_inherit);
|
|
@@ -1519,9 +1525,9 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
|
offsetofend(struct ieee80211_bssid_index, dtim_count))
|
|
elems->dtim_count = elems->bssid_index->dtim_count;
|
|
|
|
- kfree(nontransmitted_profile);
|
|
+ elems->crc = crc;
|
|
|
|
- return crc;
|
|
+ return elems;
|
|
}
|
|
|
|
void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
|
|
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
|
|
index 1382a5f..5f50ac4 100644
|
|
--- a/net/wireless/chan.c
|
|
+++ b/net/wireless/chan.c
|
|
@@ -712,6 +712,19 @@ static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
|
|
return false;
|
|
}
|
|
|
|
+static bool
|
|
+cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
|
|
+ struct ieee80211_channel *channel)
|
|
+{
|
|
+ if (!rdev->background_radar_wdev)
|
|
+ return false;
|
|
+
|
|
+ if (!cfg80211_chandef_valid(&rdev->background_radar_chandef))
|
|
+ return false;
|
|
+
|
|
+ return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel);
|
|
+}
|
|
+
|
|
bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
|
|
struct ieee80211_channel *chan)
|
|
{
|
|
@@ -728,6 +741,9 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
|
|
|
|
if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
|
|
return true;
|
|
+
|
|
+ if (cfg80211_offchan_chain_is_active(rdev, chan))
|
|
+ return true;
|
|
}
|
|
|
|
return false;
|
|
diff --git a/net/wireless/core.c b/net/wireless/core.c
|
|
index 72e010e..2ddaaae 100644
|
|
--- a/net/wireless/core.c
|
|
+++ b/net/wireless/core.c
|
|
@@ -543,6 +543,10 @@ use_default_name:
|
|
INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
|
|
INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
|
|
INIT_WORK(&rdev->event_work, cfg80211_event_work);
|
|
+ INIT_WORK(&rdev->background_cac_abort_wk,
|
|
+ cfg80211_background_cac_abort_wk);
|
|
+ INIT_DELAYED_WORK(&rdev->background_cac_done_wk,
|
|
+ cfg80211_background_cac_done_wk);
|
|
|
|
init_waitqueue_head(&rdev->dev_wait);
|
|
|
|
@@ -621,21 +625,6 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
|
|
c->limits[j].max > 1))
|
|
return -EINVAL;
|
|
|
|
- /*
|
|
- * This isn't well-defined right now. If you have an
|
|
- * IBSS interface, then its beacon interval may change
|
|
- * by joining other networks, and nothing prevents it
|
|
- * from doing that.
|
|
- * So technically we probably shouldn't even allow AP
|
|
- * and IBSS in the same interface, but it seems that
|
|
- * some drivers support that, possibly only with fixed
|
|
- * beacon intervals for IBSS.
|
|
- */
|
|
- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
|
|
- c->beacon_int_min_gcd)) {
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
cnt += c->limits[j].max;
|
|
/*
|
|
* Don't advertise an unsupported type
|
|
@@ -1052,11 +1041,13 @@ void wiphy_unregister(struct wiphy *wiphy)
|
|
cancel_work_sync(&rdev->conn_work);
|
|
flush_work(&rdev->event_work);
|
|
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
|
|
+ cancel_delayed_work_sync(&rdev->background_cac_done_wk);
|
|
flush_work(&rdev->destroy_work);
|
|
flush_work(&rdev->sched_scan_stop_wk);
|
|
flush_work(&rdev->propagate_radar_detect_wk);
|
|
flush_work(&rdev->propagate_cac_done_wk);
|
|
flush_work(&rdev->mgmt_registrations_update_wk);
|
|
+ flush_work(&rdev->background_cac_abort_wk);
|
|
|
|
#ifdef CONFIG_PM
|
|
if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
|
|
@@ -1205,6 +1196,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
|
|
|
|
cfg80211_pmsr_wdev_down(wdev);
|
|
|
|
+ cfg80211_stop_background_radar_detection(wdev);
|
|
+
|
|
switch (wdev->iftype) {
|
|
case NL80211_IFTYPE_ADHOC:
|
|
__cfg80211_leave_ibss(rdev, dev, true);
|
|
diff --git a/net/wireless/core.h b/net/wireless/core.h
|
|
index e263dd9..b588e5a 100644
|
|
--- a/net/wireless/core.h
|
|
+++ b/net/wireless/core.h
|
|
@@ -84,6 +84,11 @@ struct cfg80211_registered_device {
|
|
|
|
struct delayed_work dfs_update_channels_wk;
|
|
|
|
+ struct wireless_dev *background_radar_wdev;
|
|
+ struct cfg80211_chan_def background_radar_chandef;
|
|
+ struct delayed_work background_cac_done_wk;
|
|
+ struct work_struct background_cac_abort_wk;
|
|
+
|
|
/* netlink port which started critical protocol (0 means not started) */
|
|
u32 crit_proto_nlportid;
|
|
|
|
@@ -491,6 +496,17 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
|
|
|
|
void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
|
|
|
|
+int
|
|
+cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev,
|
|
+ struct wireless_dev *wdev,
|
|
+ struct cfg80211_chan_def *chandef);
|
|
+
|
|
+void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev);
|
|
+
|
|
+void cfg80211_background_cac_done_wk(struct work_struct *work);
|
|
+
|
|
+void cfg80211_background_cac_abort_wk(struct work_struct *work);
|
|
+
|
|
bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
|
|
struct ieee80211_channel *chan);
|
|
|
|
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
|
|
index dbe0237..00370ca 100644
|
|
--- a/net/wireless/mlme.c
|
|
+++ b/net/wireless/mlme.c
|
|
@@ -905,13 +905,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
|
|
}
|
|
|
|
|
|
-void cfg80211_radar_event(struct wiphy *wiphy,
|
|
- struct cfg80211_chan_def *chandef,
|
|
- gfp_t gfp)
|
|
+void __cfg80211_radar_event(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef,
|
|
+ bool offchan, gfp_t gfp)
|
|
{
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
|
|
|
- trace_cfg80211_radar_event(wiphy, chandef);
|
|
+ trace_cfg80211_radar_event(wiphy, chandef, offchan);
|
|
|
|
/* only set the chandef supplied channel to unavailable, in
|
|
* case the radar is detected on only one of multiple channels
|
|
@@ -919,6 +919,9 @@ void cfg80211_radar_event(struct wiphy *wiphy,
|
|
*/
|
|
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
|
|
|
|
+ if (offchan)
|
|
+ queue_work(cfg80211_wq, &rdev->background_cac_abort_wk);
|
|
+
|
|
cfg80211_sched_dfs_chan_update(rdev);
|
|
|
|
nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
|
|
@@ -926,7 +929,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
|
|
memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
|
|
queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
|
|
}
|
|
-EXPORT_SYMBOL(cfg80211_radar_event);
|
|
+EXPORT_SYMBOL(__cfg80211_radar_event);
|
|
|
|
void cfg80211_cac_event(struct net_device *netdev,
|
|
const struct cfg80211_chan_def *chandef,
|
|
@@ -970,3 +973,143 @@ void cfg80211_cac_event(struct net_device *netdev,
|
|
nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
|
|
}
|
|
EXPORT_SYMBOL(cfg80211_cac_event);
|
|
+
|
|
+static void
|
|
+__cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
|
|
+ struct wireless_dev *wdev,
|
|
+ const struct cfg80211_chan_def *chandef,
|
|
+ enum nl80211_radar_event event)
|
|
+{
|
|
+ struct wiphy *wiphy = &rdev->wiphy;
|
|
+ struct net_device *netdev;
|
|
+
|
|
+ lockdep_assert_wiphy(&rdev->wiphy);
|
|
+
|
|
+ if (!cfg80211_chandef_valid(chandef))
|
|
+ return;
|
|
+
|
|
+ if (!rdev->background_radar_wdev)
|
|
+ return;
|
|
+
|
|
+ switch (event) {
|
|
+ case NL80211_RADAR_CAC_FINISHED:
|
|
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
|
|
+ memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
|
|
+ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
|
|
+ cfg80211_sched_dfs_chan_update(rdev);
|
|
+ wdev = rdev->background_radar_wdev;
|
|
+ break;
|
|
+ case NL80211_RADAR_CAC_ABORTED:
|
|
+ if (!cancel_delayed_work(&rdev->background_cac_done_wk))
|
|
+ return;
|
|
+ wdev = rdev->background_radar_wdev;
|
|
+ break;
|
|
+ case NL80211_RADAR_CAC_STARTED:
|
|
+ break;
|
|
+ default:
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ netdev = wdev ? wdev->netdev : NULL;
|
|
+ nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
|
|
+}
|
|
+
|
|
+static void
|
|
+cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
|
|
+ const struct cfg80211_chan_def *chandef,
|
|
+ enum nl80211_radar_event event)
|
|
+{
|
|
+ wiphy_lock(&rdev->wiphy);
|
|
+ __cfg80211_background_cac_event(rdev, rdev->background_radar_wdev,
|
|
+ chandef, event);
|
|
+ wiphy_unlock(&rdev->wiphy);
|
|
+}
|
|
+
|
|
+void cfg80211_background_cac_done_wk(struct work_struct *work)
|
|
+{
|
|
+ struct delayed_work *delayed_work = to_delayed_work(work);
|
|
+ struct cfg80211_registered_device *rdev;
|
|
+
|
|
+ rdev = container_of(delayed_work, struct cfg80211_registered_device,
|
|
+ background_cac_done_wk);
|
|
+ cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef,
|
|
+ NL80211_RADAR_CAC_FINISHED);
|
|
+}
|
|
+
|
|
+void cfg80211_background_cac_abort_wk(struct work_struct *work)
|
|
+{
|
|
+ struct cfg80211_registered_device *rdev;
|
|
+
|
|
+ rdev = container_of(work, struct cfg80211_registered_device,
|
|
+ background_cac_abort_wk);
|
|
+ cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef,
|
|
+ NL80211_RADAR_CAC_ABORTED);
|
|
+}
|
|
+
|
|
+void cfg80211_background_cac_abort(struct wiphy *wiphy)
|
|
+{
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
|
+
|
|
+ queue_work(cfg80211_wq, &rdev->background_cac_abort_wk);
|
|
+}
|
|
+EXPORT_SYMBOL(cfg80211_background_cac_abort);
|
|
+
|
|
+int
|
|
+cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev,
|
|
+ struct wireless_dev *wdev,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ unsigned int cac_time_ms;
|
|
+ int err;
|
|
+
|
|
+ lockdep_assert_wiphy(&rdev->wiphy);
|
|
+
|
|
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
|
|
+ NL80211_EXT_FEATURE_RADAR_BACKGROUND))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ /* Offchannel chain already locked by another wdev */
|
|
+ if (rdev->background_radar_wdev && rdev->background_radar_wdev != wdev)
|
|
+ return -EBUSY;
|
|
+
|
|
+ /* CAC already in progress on the offchannel chain */
|
|
+ if (rdev->background_radar_wdev == wdev &&
|
|
+ delayed_work_pending(&rdev->background_cac_done_wk))
|
|
+ return -EBUSY;
|
|
+
|
|
+ err = rdev_set_radar_background(rdev, chandef);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef);
|
|
+ if (!cac_time_ms)
|
|
+ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
|
|
+
|
|
+ rdev->background_radar_chandef = *chandef;
|
|
+ rdev->background_radar_wdev = wdev; /* Get offchain ownership */
|
|
+
|
|
+ __cfg80211_background_cac_event(rdev, wdev, chandef,
|
|
+ NL80211_RADAR_CAC_STARTED);
|
|
+ queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk,
|
|
+ msecs_to_jiffies(cac_time_ms));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
|
|
+{
|
|
+ struct wiphy *wiphy = wdev->wiphy;
|
|
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
|
+
|
|
+ lockdep_assert_wiphy(wiphy);
|
|
+
|
|
+ if (wdev != rdev->background_radar_wdev)
|
|
+ return;
|
|
+
|
|
+ rdev_set_radar_background(rdev, NULL);
|
|
+ rdev->background_radar_wdev = NULL; /* Release offchain ownership */
|
|
+
|
|
+ __cfg80211_background_cac_event(rdev, wdev,
|
|
+ &rdev->background_radar_chandef,
|
|
+ NL80211_RADAR_CAC_ABORTED);
|
|
+}
|
|
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
|
|
index 20df12c..bc6b5ac 100644
|
|
--- a/net/wireless/nl80211.c
|
|
+++ b/net/wireless/nl80211.c
|
|
@@ -442,6 +442,16 @@ sar_policy[NL80211_SAR_ATTR_MAX + 1] = {
|
|
[NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy),
|
|
};
|
|
|
|
+static const struct nla_policy
|
|
+nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
|
|
+ [NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2),
|
|
+ [NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] =
|
|
+ NLA_POLICY_MIN(NLA_U8, 1),
|
|
+ [NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 },
|
|
+ [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
|
|
+ [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
|
|
+};
|
|
+
|
|
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
|
|
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
|
@@ -788,6 +798,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
[NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
|
|
+ [NL80211_ATTR_MBSSID_CONFIG] =
|
|
+ NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
|
|
+ [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
|
|
+ [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
|
|
+ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
|
|
};
|
|
|
|
/* policy for the key attributes */
|
|
@@ -2236,6 +2251,35 @@ fail:
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
+static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg)
|
|
+{
|
|
+ struct nlattr *config;
|
|
+
|
|
+ if (!wiphy->mbssid_max_interfaces)
|
|
+ return 0;
|
|
+
|
|
+ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
|
|
+ if (!config)
|
|
+ return -ENOBUFS;
|
|
+
|
|
+ if (nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
|
|
+ wiphy->mbssid_max_interfaces))
|
|
+ goto fail;
|
|
+
|
|
+ if (wiphy->ema_max_profile_periodicity &&
|
|
+ nla_put_u8(msg,
|
|
+ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
|
|
+ wiphy->ema_max_profile_periodicity))
|
|
+ goto fail;
|
|
+
|
|
+ nla_nest_end(msg, config);
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ nla_nest_cancel(msg, config);
|
|
+ return -ENOBUFS;
|
|
+}
|
|
+
|
|
struct nl80211_dump_wiphy_state {
|
|
s64 filter_wiphy;
|
|
long start;
|
|
@@ -2821,6 +2865,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
|
if (nl80211_put_sar_specs(rdev, msg))
|
|
goto nla_put_failure;
|
|
|
|
+ if (nl80211_put_mbssid_support(&rdev->wiphy, msg))
|
|
+ goto nla_put_failure;
|
|
+
|
|
/* done */
|
|
state->split_start = 0;
|
|
break;
|
|
@@ -3346,6 +3393,22 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|
goto out;
|
|
}
|
|
|
|
+ if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) {
|
|
+ int idx, dbi = 0;
|
|
+
|
|
+ if (!rdev->ops->set_antenna_gain) {
|
|
+ result = -EOPNOTSUPP;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ idx = NL80211_ATTR_WIPHY_ANTENNA_GAIN;
|
|
+ dbi = nla_get_u32(info->attrs[idx]);
|
|
+
|
|
+ result = rdev->ops->set_antenna_gain(&rdev->wiphy, dbi);
|
|
+ if (result)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
|
|
struct wireless_dev *txp_wdev = wdev;
|
|
enum nl80211_tx_power_setting type;
|
|
@@ -5020,6 +5083,96 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
|
|
return 0;
|
|
}
|
|
|
|
+static int nl80211_parse_mbssid_config(struct wiphy *wiphy,
|
|
+ struct net_device *dev,
|
|
+ struct nlattr *attrs,
|
|
+ struct cfg80211_mbssid_config *config,
|
|
+ u8 num_elems)
|
|
+{
|
|
+ struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
|
|
+
|
|
+ if (!wiphy->mbssid_max_interfaces)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (nla_parse_nested(tb, NL80211_MBSSID_CONFIG_ATTR_MAX, attrs, NULL,
|
|
+ NULL) ||
|
|
+ !tb[NL80211_MBSSID_CONFIG_ATTR_INDEX])
|
|
+ return -EINVAL;
|
|
+
|
|
+ config->ema = nla_get_flag(tb[NL80211_MBSSID_CONFIG_ATTR_EMA]);
|
|
+ if (config->ema) {
|
|
+ if (!wiphy->ema_max_profile_periodicity)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (num_elems > wiphy->ema_max_profile_periodicity)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ config->index = nla_get_u8(tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]);
|
|
+ if (config->index >= wiphy->mbssid_max_interfaces ||
|
|
+ (!config->index && !num_elems))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]) {
|
|
+ u32 tx_ifindex =
|
|
+ nla_get_u32(tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]);
|
|
+
|
|
+ if ((!config->index && tx_ifindex != dev->ifindex) ||
|
|
+ (config->index && tx_ifindex == dev->ifindex))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (tx_ifindex != dev->ifindex) {
|
|
+ struct net_device *tx_netdev =
|
|
+ dev_get_by_index(wiphy_net(wiphy), tx_ifindex);
|
|
+
|
|
+ if (!tx_netdev || !tx_netdev->ieee80211_ptr ||
|
|
+ tx_netdev->ieee80211_ptr->wiphy != wiphy ||
|
|
+ tx_netdev->ieee80211_ptr->iftype !=
|
|
+ NL80211_IFTYPE_AP) {
|
|
+ dev_put(tx_netdev);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ config->tx_wdev = tx_netdev->ieee80211_ptr;
|
|
+ } else {
|
|
+ config->tx_wdev = dev->ieee80211_ptr;
|
|
+ }
|
|
+ } else if (!config->index) {
|
|
+ config->tx_wdev = dev->ieee80211_ptr;
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct cfg80211_mbssid_elems *
|
|
+nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
|
|
+{
|
|
+ struct nlattr *nl_elems;
|
|
+ struct cfg80211_mbssid_elems *elems;
|
|
+ int rem_elems;
|
|
+ u8 i = 0, num_elems = 0;
|
|
+
|
|
+ if (!wiphy->mbssid_max_interfaces)
|
|
+ return ERR_PTR(-EINVAL);
|
|
+
|
|
+ nla_for_each_nested(nl_elems, attrs, rem_elems)
|
|
+ num_elems++;
|
|
+
|
|
+ elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL);
|
|
+ if (!elems)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ nla_for_each_nested(nl_elems, attrs, rem_elems) {
|
|
+ elems->elem[i].data = nla_data(nl_elems);
|
|
+ elems->elem[i].len = nla_len(nl_elems);
|
|
+ i++;
|
|
+ }
|
|
+ elems->cnt = num_elems;
|
|
+ return elems;
|
|
+}
|
|
+
|
|
static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
|
struct nlattr *attrs[],
|
|
struct cfg80211_beacon_data *bcn)
|
|
@@ -5100,6 +5253,17 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
|
bcn->ftm_responder = -1;
|
|
}
|
|
|
|
+ if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
|
|
+ struct cfg80211_mbssid_elems *mbssid =
|
|
+ nl80211_parse_mbssid_elems(&rdev->wiphy,
|
|
+ attrs[NL80211_ATTR_MBSSID_ELEMS]);
|
|
+
|
|
+ if (IS_ERR(mbssid))
|
|
+ return PTR_ERR(mbssid);
|
|
+
|
|
+ bcn->mbssid_ies = mbssid;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -5556,6 +5720,17 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|
goto out;
|
|
}
|
|
|
|
+ if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) {
|
|
+ err = nl80211_parse_mbssid_config(&rdev->wiphy, dev,
|
|
+ info->attrs[NL80211_ATTR_MBSSID_CONFIG],
|
|
+ ¶ms.mbssid_config,
|
|
+ params.beacon.mbssid_ies ?
|
|
+ params.beacon.mbssid_ies->cnt :
|
|
+ 0);
|
|
+ if (err)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
nl80211_calculate_ap_params(¶ms);
|
|
|
|
if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
|
|
@@ -5577,6 +5752,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
out:
|
|
kfree(params.acl);
|
|
+ kfree(params.beacon.mbssid_ies);
|
|
+ if (params.mbssid_config.tx_wdev &&
|
|
+ params.mbssid_config.tx_wdev->netdev &&
|
|
+ params.mbssid_config.tx_wdev->netdev != dev)
|
|
+ dev_put(params.mbssid_config.tx_wdev->netdev);
|
|
|
|
return err;
|
|
}
|
|
@@ -5601,12 +5781,14 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms);
|
|
if (err)
|
|
- return err;
|
|
+ goto out;
|
|
|
|
wdev_lock(wdev);
|
|
err = rdev_change_beacon(rdev, dev, ¶ms);
|
|
wdev_unlock(wdev);
|
|
|
|
+out:
|
|
+ kfree(params.mbssid_ies);
|
|
return err;
|
|
}
|
|
|
|
@@ -9113,38 +9295,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
|
|
struct cfg80211_chan_def chandef;
|
|
enum nl80211_dfs_regions dfs_region;
|
|
unsigned int cac_time_ms;
|
|
- int err;
|
|
+ int err = -EINVAL;
|
|
+
|
|
+ flush_delayed_work(&rdev->dfs_update_channels_wk);
|
|
+
|
|
+ wiphy_lock(wiphy);
|
|
|
|
dfs_region = reg_get_dfs_region(wiphy);
|
|
if (dfs_region == NL80211_DFS_UNSET)
|
|
- return -EINVAL;
|
|
+ goto unlock;
|
|
|
|
err = nl80211_parse_chandef(rdev, info, &chandef);
|
|
if (err)
|
|
- return err;
|
|
-
|
|
- if (netif_carrier_ok(dev))
|
|
- return -EBUSY;
|
|
-
|
|
- if (wdev->cac_started)
|
|
- return -EBUSY;
|
|
+ goto unlock;
|
|
|
|
err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
|
|
if (err < 0)
|
|
- return err;
|
|
+ goto unlock;
|
|
|
|
- if (err == 0)
|
|
- return -EINVAL;
|
|
+ if (err == 0) {
|
|
+ err = -EINVAL;
|
|
+ goto unlock;
|
|
+ }
|
|
|
|
- if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
|
|
- return -EINVAL;
|
|
+ if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) {
|
|
+ err = -EINVAL;
|
|
+ goto unlock;
|
|
+ }
|
|
+
|
|
+ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_BACKGROUND])) {
|
|
+ err = cfg80211_start_background_radar_detection(rdev, wdev,
|
|
+ &chandef);
|
|
+ goto unlock;
|
|
+ }
|
|
+
|
|
+ if (netif_carrier_ok(dev)) {
|
|
+ err = -EBUSY;
|
|
+ goto unlock;
|
|
+ }
|
|
+
|
|
+ if (wdev->cac_started) {
|
|
+ err = -EBUSY;
|
|
+ goto unlock;
|
|
+ }
|
|
|
|
/* CAC start is offloaded to HW and can't be started manually */
|
|
- if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
|
|
- return -EOPNOTSUPP;
|
|
+ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) {
|
|
+ err = -EOPNOTSUPP;
|
|
+ goto unlock;
|
|
+ }
|
|
|
|
- if (!rdev->ops->start_radar_detection)
|
|
- return -EOPNOTSUPP;
|
|
+ if (!rdev->ops->start_radar_detection) {
|
|
+ err = -EOPNOTSUPP;
|
|
+ goto unlock;
|
|
+ }
|
|
|
|
cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
|
|
if (WARN_ON(!cac_time_ms))
|
|
@@ -9157,6 +9361,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
|
|
wdev->cac_start_time = jiffies;
|
|
wdev->cac_time_ms = cac_time_ms;
|
|
}
|
|
+unlock:
|
|
+ wiphy_unlock(wiphy);
|
|
+
|
|
return err;
|
|
}
|
|
|
|
@@ -9283,12 +9490,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after);
|
|
if (err)
|
|
- return err;
|
|
+ goto free;
|
|
|
|
csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs),
|
|
GFP_KERNEL);
|
|
- if (!csa_attrs)
|
|
- return -ENOMEM;
|
|
+ if (!csa_attrs) {
|
|
+ err = -ENOMEM;
|
|
+ goto free;
|
|
+ }
|
|
|
|
err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX,
|
|
info->attrs[NL80211_ATTR_CSA_IES],
|
|
@@ -9407,6 +9616,8 @@ skip_beacons:
|
|
wdev_unlock(wdev);
|
|
|
|
free:
|
|
+ kfree(params.beacon_after.mbssid_ies);
|
|
+ kfree(params.beacon_csa.mbssid_ies);
|
|
kfree(csa_attrs);
|
|
return err;
|
|
}
|
|
@@ -14959,6 +15170,8 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
|
|
wdev_unlock(wdev);
|
|
|
|
out:
|
|
+ kfree(params.beacon_next.mbssid_ies);
|
|
+ kfree(params.beacon_color_change.mbssid_ies);
|
|
kfree(tb);
|
|
return err;
|
|
}
|
|
@@ -15786,7 +15999,8 @@ static const struct genl_small_ops nl80211_small_ops[] = {
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
|
.doit = nl80211_start_radar_detection,
|
|
.flags = GENL_UNS_ADMIN_PERM,
|
|
- .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
+ NL80211_FLAG_NO_WIPHY_MTX,
|
|
},
|
|
{
|
|
.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
|
|
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
|
|
index 36422cf..8555468 100644
|
|
--- a/net/wireless/rdev-ops.h
|
|
+++ b/net/wireless/rdev-ops.h
|
|
@@ -1381,4 +1381,21 @@ static inline int rdev_color_change(struct cfg80211_registered_device *rdev,
|
|
return ret;
|
|
}
|
|
|
|
+static inline int
|
|
+rdev_set_radar_background(struct cfg80211_registered_device *rdev,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ struct wiphy *wiphy = &rdev->wiphy;
|
|
+ int ret;
|
|
+
|
|
+ if (!rdev->ops->set_radar_background)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ trace_rdev_set_radar_background(wiphy, chandef);
|
|
+ ret = rdev->ops->set_radar_background(wiphy, chandef);
|
|
+ trace_rdev_return_int(wiphy, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
#endif /* __CFG80211_RDEV_OPS */
|
|
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
|
|
index 10ba8ca..43e7cba 100644
|
|
--- a/net/wireless/scan.c
|
|
+++ b/net/wireless/scan.c
|
|
@@ -143,18 +143,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
|
|
lockdep_assert_held(&rdev->bss_lock);
|
|
|
|
bss->refcount++;
|
|
- if (bss->pub.hidden_beacon_bss) {
|
|
- bss = container_of(bss->pub.hidden_beacon_bss,
|
|
- struct cfg80211_internal_bss,
|
|
- pub);
|
|
- bss->refcount++;
|
|
- }
|
|
- if (bss->pub.transmitted_bss) {
|
|
- bss = container_of(bss->pub.transmitted_bss,
|
|
- struct cfg80211_internal_bss,
|
|
- pub);
|
|
- bss->refcount++;
|
|
- }
|
|
+
|
|
+ if (bss->pub.hidden_beacon_bss)
|
|
+ bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++;
|
|
+
|
|
+ if (bss->pub.transmitted_bss)
|
|
+ bss_from_pub(bss->pub.transmitted_bss)->refcount++;
|
|
}
|
|
|
|
static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
|
|
@@ -304,7 +298,8 @@ static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
|
|
tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
|
|
tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
|
|
|
|
- while (tmp_old + tmp_old[1] + 2 - ie <= ielen) {
|
|
+ while (tmp_old + 2 - ie <= ielen &&
|
|
+ tmp_old + tmp_old[1] + 2 - ie <= ielen) {
|
|
if (tmp_old[0] == 0) {
|
|
tmp_old++;
|
|
continue;
|
|
@@ -364,7 +359,8 @@ static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
|
|
* copied to new ie, skip ssid, capability, bssid-index ie
|
|
*/
|
|
tmp_new = sub_copy;
|
|
- while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
|
|
+ while (tmp_new + 2 - sub_copy <= subie_len &&
|
|
+ tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
|
|
if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
|
|
tmp_new[0] == WLAN_EID_SSID)) {
|
|
memcpy(pos, tmp_new, tmp_new[1] + 2);
|
|
@@ -429,6 +425,15 @@ cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss,
|
|
|
|
rcu_read_unlock();
|
|
|
|
+ /*
|
|
+ * This is a bit weird - it's not on the list, but already on another
|
|
+ * one! The only way that could happen is if there's some BSSID/SSID
|
|
+ * shared by multiple APs in their multi-BSSID profiles, potentially
|
|
+ * with hidden SSID mixed in ... ignore it.
|
|
+ */
|
|
+ if (!list_empty(&nontrans_bss->nontrans_list))
|
|
+ return -EINVAL;
|
|
+
|
|
/* add to the list */
|
|
list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
|
|
return 0;
|
|
@@ -1604,6 +1609,23 @@ struct cfg80211_non_tx_bss {
|
|
u8 bssid_index;
|
|
};
|
|
|
|
+static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known,
|
|
+ const struct cfg80211_bss_ies *new_ies,
|
|
+ const struct cfg80211_bss_ies *old_ies)
|
|
+{
|
|
+ struct cfg80211_internal_bss *bss;
|
|
+
|
|
+ /* Assign beacon IEs to all sub entries */
|
|
+ list_for_each_entry(bss, &known->hidden_list, hidden_list) {
|
|
+ const struct cfg80211_bss_ies *ies;
|
|
+
|
|
+ ies = rcu_access_pointer(bss->pub.beacon_ies);
|
|
+ WARN_ON(ies != old_ies);
|
|
+
|
|
+ rcu_assign_pointer(bss->pub.beacon_ies, new_ies);
|
|
+ }
|
|
+}
|
|
+
|
|
static bool
|
|
cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
|
|
struct cfg80211_internal_bss *known,
|
|
@@ -1627,7 +1649,6 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
|
|
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
|
} else if (rcu_access_pointer(new->pub.beacon_ies)) {
|
|
const struct cfg80211_bss_ies *old;
|
|
- struct cfg80211_internal_bss *bss;
|
|
|
|
if (known->pub.hidden_beacon_bss &&
|
|
!list_empty(&known->hidden_list)) {
|
|
@@ -1655,16 +1676,7 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
|
|
if (old == rcu_access_pointer(known->pub.ies))
|
|
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
|
|
|
|
- /* Assign beacon IEs to all sub entries */
|
|
- list_for_each_entry(bss, &known->hidden_list, hidden_list) {
|
|
- const struct cfg80211_bss_ies *ies;
|
|
-
|
|
- ies = rcu_access_pointer(bss->pub.beacon_ies);
|
|
- WARN_ON(ies != old);
|
|
-
|
|
- rcu_assign_pointer(bss->pub.beacon_ies,
|
|
- new->pub.beacon_ies);
|
|
- }
|
|
+ cfg80211_update_hidden_bsses(known, new->pub.beacon_ies, old);
|
|
|
|
if (old)
|
|
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
|
@@ -1741,6 +1753,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
|
|
new->refcount = 1;
|
|
INIT_LIST_HEAD(&new->hidden_list);
|
|
INIT_LIST_HEAD(&new->pub.nontrans_list);
|
|
+ /* we'll set this later if it was non-NULL */
|
|
+ new->pub.transmitted_bss = NULL;
|
|
|
|
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
|
|
hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
|
|
@@ -1981,10 +1995,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
|
|
spin_lock_bh(&rdev->bss_lock);
|
|
if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
|
|
&res->pub)) {
|
|
- if (__cfg80211_unlink_bss(rdev, res))
|
|
+ if (__cfg80211_unlink_bss(rdev, res)) {
|
|
rdev->bss_generation++;
|
|
+ res = NULL;
|
|
+ }
|
|
}
|
|
spin_unlock_bh(&rdev->bss_lock);
|
|
+
|
|
+ if (!res)
|
|
+ return NULL;
|
|
}
|
|
|
|
trace_cfg80211_return_bss(&res->pub);
|
|
@@ -2103,6 +2122,8 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
|
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
|
|
if (elem->datalen < 4)
|
|
continue;
|
|
+ if (elem->data[0] < 1 || (int)elem->data[0] > 8)
|
|
+ continue;
|
|
for_each_element(sub, elem->data + 1, elem->datalen - 1) {
|
|
u8 profile_len;
|
|
|
|
@@ -2238,7 +2259,7 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
|
|
size_t new_ie_len;
|
|
struct cfg80211_bss_ies *new_ies;
|
|
const struct cfg80211_bss_ies *old;
|
|
- u8 cpy_len;
|
|
+ size_t cpy_len;
|
|
|
|
lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock);
|
|
|
|
@@ -2305,6 +2326,8 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
|
|
} else {
|
|
old = rcu_access_pointer(nontrans_bss->beacon_ies);
|
|
rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies);
|
|
+ cfg80211_update_hidden_bsses(bss_from_pub(nontrans_bss),
|
|
+ new_ies, old);
|
|
rcu_assign_pointer(nontrans_bss->ies, new_ies);
|
|
if (old)
|
|
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
|
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
|
|
index 0c3f05c..30c0dcd 100644
|
|
--- a/net/wireless/sysfs.c
|
|
+++ b/net/wireless/sysfs.c
|
|
@@ -24,18 +24,35 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
|
|
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
|
|
}
|
|
|
|
-#define SHOW_FMT(name, fmt, member) \
|
|
+#define SHOW_FMT(name, fmt, member, mode) \
|
|
static ssize_t name ## _show(struct device *dev, \
|
|
struct device_attribute *attr, \
|
|
char *buf) \
|
|
{ \
|
|
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
|
|
} \
|
|
-static DEVICE_ATTR_RO(name)
|
|
+static DEVICE_ATTR_##mode(name)
|
|
|
|
-SHOW_FMT(index, "%d", wiphy_idx);
|
|
-SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
|
|
-SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
|
|
+static ssize_t macaddress_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t len)
|
|
+{
|
|
+ u8 mac[ETH_ALEN];
|
|
+
|
|
+ if (!mac_pton(buf, mac))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
|
|
+
|
|
+ return strnlen(buf, len);
|
|
+}
|
|
+
|
|
+SHOW_FMT(index, "%d", wiphy_idx, RO);
|
|
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
|
|
+SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
|
|
|
|
static ssize_t name_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
|
|
index 973ce68..97a2937 100644
|
|
--- a/net/wireless/trace.h
|
|
+++ b/net/wireless/trace.h
|
|
@@ -3022,18 +3022,21 @@ TRACE_EVENT(cfg80211_ch_switch_started_notify,
|
|
);
|
|
|
|
TRACE_EVENT(cfg80211_radar_event,
|
|
- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
|
|
- TP_ARGS(wiphy, chandef),
|
|
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
|
|
+ bool offchan),
|
|
+ TP_ARGS(wiphy, chandef, offchan),
|
|
TP_STRUCT__entry(
|
|
WIPHY_ENTRY
|
|
CHAN_DEF_ENTRY
|
|
+ __field(bool, offchan)
|
|
),
|
|
TP_fast_assign(
|
|
WIPHY_ASSIGN;
|
|
CHAN_DEF_ASSIGN(chandef);
|
|
+ __entry->offchan = offchan;
|
|
),
|
|
- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
|
|
- WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
|
|
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", offchan %d",
|
|
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->offchan)
|
|
);
|
|
|
|
TRACE_EVENT(cfg80211_cac_event,
|
|
@@ -3643,6 +3646,25 @@ TRACE_EVENT(cfg80211_bss_color_notify,
|
|
__entry->color_bitmap)
|
|
);
|
|
|
|
+TRACE_EVENT(rdev_set_radar_background,
|
|
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
|
|
+
|
|
+ TP_ARGS(wiphy, chandef),
|
|
+
|
|
+ TP_STRUCT__entry(
|
|
+ WIPHY_ENTRY
|
|
+ CHAN_DEF_ENTRY
|
|
+ ),
|
|
+
|
|
+ TP_fast_assign(
|
|
+ WIPHY_ASSIGN;
|
|
+ CHAN_DEF_ASSIGN(chandef)
|
|
+ ),
|
|
+
|
|
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
|
|
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
|
|
+);
|
|
+
|
|
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
|
|
|
|
#undef TRACE_INCLUDE_PATH
|