Here are quite a large number of fixes, notably:
* various A-MSDU building fixes (currently only affects mt76) * syzkaller & spectre fixes in hwsim * TXQ vs. teardown fix that was causing crashes * embed WMM info in reg rule, bad code here had been causing crashes * one compilation issue with fix from Arnd (rfkill-gpio includes) * fixes for a race and bad data during/after channel switch * nl80211: a validation fix, attribute type & unit fixes along with other small fixes. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEH1e1rEeCd0AIMq6MB8qZga/fl8QFAluNJXcACgkQB8qZga/f l8Qvfw//dBwlhMII862Evk4M8OzhdHfkJ4Kp/d2C476whbEySU/jRIIeetmVpXYV 5cfStTxBpGkwMj5PXy3DaA2PO++L5qaApDJfHc8DNWNmvt9rRRJul1zP05HjZRxW G7aFCFRWVK0dlmVP9GC/b20KyUvz4OpklBnxylkIrx0FCkw5bAHs1SsjGZCg/6Tm 008DAhFz3Ds6hNLxwricvrk5oQ6eC1cDfDd4Rtk3jCYQ4t7KFn5gFoKzKldfLdWe TFTpVQ26XAGzn9QVXzAiXN4ZNpUpZrFXosC7cn5Ugiyic4YtnHxS2wVDuL3vs1cL J2hoW6wjEBg+U6vmHMcijo1lnQwW7ueYUDWLJPNIXHA6A7sGyA6z6D7vbbvHfoG6 L681BrYmTmKkXXquu5+r85/9WgP2cmzbRpoIxTQl3sU2Liw2k5IJ9ryLLyul+8z7 spnDPOY7h4c0JrAvhjHkrKIbbW4FKYunxZJ8dn9eyAzOd/58iKoXzu4yAggwm+0V DtZiu0gSr52sKrh1vqEyfhrPFCN1Mc19DRsJBtabUfVEveQTwToCkbZ5s1sLqSId m30XUjjYOiRk7MZnncar0lE4//eJ6bnL3Wie3UTmO3xsMwlgKQPqjI4TprNogUCk R2dVeGmhm3HSriRHKJL3/D8uzw5mMBI3Kicw9tFSSyVjtJgxvpg= =lLBA -----END PGP SIGNATURE----- Merge tag 'mac80211-for-davem-2018-09-03' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211 Johannes Berg says: ==================== Here are quite a large number of fixes, notably: * various A-MSDU building fixes (currently only affects mt76) * syzkaller & spectre fixes in hwsim * TXQ vs. teardown fix that was causing crashes * embed WMM info in reg rule, bad code here had been causing crashes * one compilation issue with fix from Arnd (rfkill-gpio includes) * fixes for a race and bad data during/after channel switch * nl80211: a validation fix, attribute type & unit fixes along with other small fixes. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
fc3e3bf55f
|
@ -985,15 +985,12 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
|
|||
const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ?
|
||||
iwl_ext_nvm_channels : iwl_nvm_channels;
|
||||
struct ieee80211_regdomain *regd, *copy_rd;
|
||||
int size_of_regd, regd_to_copy, wmms_to_copy;
|
||||
int size_of_wmms = 0;
|
||||
int size_of_regd, regd_to_copy;
|
||||
struct ieee80211_reg_rule *rule;
|
||||
struct ieee80211_wmm_rule *wmm_rule, *d_wmm, *s_wmm;
|
||||
struct regdb_ptrs *regdb_ptrs;
|
||||
enum nl80211_band band;
|
||||
int center_freq, prev_center_freq = 0;
|
||||
int valid_rules = 0, n_wmms = 0;
|
||||
int i;
|
||||
int valid_rules = 0;
|
||||
bool new_rule;
|
||||
int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ?
|
||||
IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS;
|
||||
|
@ -1012,11 +1009,7 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
|
|||
sizeof(struct ieee80211_regdomain) +
|
||||
num_of_ch * sizeof(struct ieee80211_reg_rule);
|
||||
|
||||
if (geo_info & GEO_WMM_ETSI_5GHZ_INFO)
|
||||
size_of_wmms =
|
||||
num_of_ch * sizeof(struct ieee80211_wmm_rule);
|
||||
|
||||
regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL);
|
||||
regd = kzalloc(size_of_regd, GFP_KERNEL);
|
||||
if (!regd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -1030,8 +1023,6 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
|
|||
regd->alpha2[0] = fw_mcc >> 8;
|
||||
regd->alpha2[1] = fw_mcc & 0xff;
|
||||
|
||||
wmm_rule = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
|
||||
|
||||
for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
|
||||
ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
|
||||
band = (ch_idx < NUM_2GHZ_CHANNELS) ?
|
||||
|
@ -1085,26 +1076,10 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
|
|||
band == NL80211_BAND_2GHZ)
|
||||
continue;
|
||||
|
||||
if (!reg_query_regdb_wmm(regd->alpha2, center_freq,
|
||||
®db_ptrs[n_wmms].token, wmm_rule)) {
|
||||
/* Add only new rules */
|
||||
for (i = 0; i < n_wmms; i++) {
|
||||
if (regdb_ptrs[i].token ==
|
||||
regdb_ptrs[n_wmms].token) {
|
||||
rule->wmm_rule = regdb_ptrs[i].rule;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == n_wmms) {
|
||||
rule->wmm_rule = wmm_rule;
|
||||
regdb_ptrs[n_wmms++].rule = wmm_rule;
|
||||
wmm_rule++;
|
||||
}
|
||||
}
|
||||
reg_query_regdb_wmm(regd->alpha2, center_freq, rule);
|
||||
}
|
||||
|
||||
regd->n_reg_rules = valid_rules;
|
||||
regd->n_wmm_rules = n_wmms;
|
||||
|
||||
/*
|
||||
* Narrow down regdom for unused regulatory rules to prevent hole
|
||||
|
@ -1113,28 +1088,13 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
|
|||
regd_to_copy = sizeof(struct ieee80211_regdomain) +
|
||||
valid_rules * sizeof(struct ieee80211_reg_rule);
|
||||
|
||||
wmms_to_copy = sizeof(struct ieee80211_wmm_rule) * n_wmms;
|
||||
|
||||
copy_rd = kzalloc(regd_to_copy + wmms_to_copy, GFP_KERNEL);
|
||||
copy_rd = kzalloc(regd_to_copy, GFP_KERNEL);
|
||||
if (!copy_rd) {
|
||||
copy_rd = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(copy_rd, regd, regd_to_copy);
|
||||
memcpy((u8 *)copy_rd + regd_to_copy, (u8 *)regd + size_of_regd,
|
||||
wmms_to_copy);
|
||||
|
||||
d_wmm = (struct ieee80211_wmm_rule *)((u8 *)copy_rd + regd_to_copy);
|
||||
s_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
|
||||
|
||||
for (i = 0; i < regd->n_reg_rules; i++) {
|
||||
if (!regd->reg_rules[i].wmm_rule)
|
||||
continue;
|
||||
|
||||
copy_rd->reg_rules[i].wmm_rule = d_wmm +
|
||||
(regd->reg_rules[i].wmm_rule - s_wmm);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(regdb_ptrs);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
#include <linux/rhashtable.h>
|
||||
#include <linux/nospec.h>
|
||||
#include "mac80211_hwsim.h"
|
||||
|
||||
#define WARN_QUEUE 100
|
||||
|
@ -2820,9 +2821,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
|||
IEEE80211_VHT_CAP_SHORT_GI_80 |
|
||||
IEEE80211_VHT_CAP_SHORT_GI_160 |
|
||||
IEEE80211_VHT_CAP_TXSTBC |
|
||||
IEEE80211_VHT_CAP_RXSTBC_1 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_2 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_3 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_4 |
|
||||
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
|
||||
sband->vht_cap.vht_mcs.rx_mcs_map =
|
||||
|
@ -3317,6 +3315,11 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
|||
if (info->attrs[HWSIM_ATTR_CHANNELS])
|
||||
param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
|
||||
|
||||
if (param.channels < 1) {
|
||||
GENL_SET_ERR_MSG(info, "must have at least one channel");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (param.channels > CFG80211_MAX_NUM_DIFFERENT_CHANNELS) {
|
||||
GENL_SET_ERR_MSG(info, "too many channels specified");
|
||||
return -EINVAL;
|
||||
|
@ -3350,6 +3353,9 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
|||
kfree(hwname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
idx = array_index_nospec(idx,
|
||||
ARRAY_SIZE(hwsim_world_regdom_custom));
|
||||
param.regd = hwsim_world_regdom_custom[idx];
|
||||
}
|
||||
|
||||
|
|
|
@ -4865,8 +4865,8 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator);
|
|||
*
|
||||
* Return: 0 on success. -ENODATA.
|
||||
*/
|
||||
int reg_query_regdb_wmm(char *alpha2, int freq, u32 *ptr,
|
||||
struct ieee80211_wmm_rule *rule);
|
||||
int reg_query_regdb_wmm(char *alpha2, int freq,
|
||||
struct ieee80211_reg_rule *rule);
|
||||
|
||||
/*
|
||||
* callbacks for asynchronous cfg80211 methods, notification
|
||||
|
|
|
@ -217,15 +217,15 @@ struct ieee80211_wmm_rule {
|
|||
struct ieee80211_reg_rule {
|
||||
struct ieee80211_freq_range freq_range;
|
||||
struct ieee80211_power_rule power_rule;
|
||||
struct ieee80211_wmm_rule *wmm_rule;
|
||||
struct ieee80211_wmm_rule wmm_rule;
|
||||
u32 flags;
|
||||
u32 dfs_cac_ms;
|
||||
bool has_wmm;
|
||||
};
|
||||
|
||||
struct ieee80211_regdomain {
|
||||
struct rcu_head rcu_head;
|
||||
u32 n_reg_rules;
|
||||
u32 n_wmm_rules;
|
||||
char alpha2[3];
|
||||
enum nl80211_dfs_regions dfs_region;
|
||||
struct ieee80211_reg_rule reg_rules[];
|
||||
|
|
|
@ -947,8 +947,8 @@ static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
|
|||
if (len < IEEE80211_DEAUTH_FRAME_LEN)
|
||||
return;
|
||||
|
||||
ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n",
|
||||
mgmt->sa, mgmt->da, mgmt->bssid, reason);
|
||||
ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM\n", mgmt->sa, mgmt->da);
|
||||
ibss_dbg(sdata, "\tBSSID=%pM (reason: %d)\n", mgmt->bssid, reason);
|
||||
sta_info_destroy_addr(sdata, mgmt->sa);
|
||||
}
|
||||
|
||||
|
@ -966,9 +966,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
|
|||
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
|
||||
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
|
||||
|
||||
ibss_dbg(sdata,
|
||||
"RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
|
||||
mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
|
||||
ibss_dbg(sdata, "RX Auth SA=%pM DA=%pM\n", mgmt->sa, mgmt->da);
|
||||
ibss_dbg(sdata, "\tBSSID=%pM (auth_transaction=%d)\n",
|
||||
mgmt->bssid, auth_transaction);
|
||||
|
||||
if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
|
||||
return;
|
||||
|
@ -1175,10 +1175,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
|||
rx_timestamp = drv_get_tsf(local, sdata);
|
||||
}
|
||||
|
||||
ibss_dbg(sdata,
|
||||
"RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
|
||||
ibss_dbg(sdata, "RX beacon SA=%pM BSSID=%pM TSF=0x%llx\n",
|
||||
mgmt->sa, mgmt->bssid,
|
||||
(unsigned long long)rx_timestamp,
|
||||
(unsigned long long)rx_timestamp);
|
||||
ibss_dbg(sdata, "\tBCN=0x%llx diff=%lld @%lu\n",
|
||||
(unsigned long long)beacon_timestamp,
|
||||
(unsigned long long)(rx_timestamp - beacon_timestamp),
|
||||
jiffies);
|
||||
|
@ -1537,9 +1537,9 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
tx_last_beacon = drv_tx_last_beacon(local);
|
||||
|
||||
ibss_dbg(sdata,
|
||||
"RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n",
|
||||
mgmt->sa, mgmt->da, mgmt->bssid, tx_last_beacon);
|
||||
ibss_dbg(sdata, "RX ProbeReq SA=%pM DA=%pM\n", mgmt->sa, mgmt->da);
|
||||
ibss_dbg(sdata, "\tBSSID=%pM (tx_last_beacon=%d)\n",
|
||||
mgmt->bssid, tx_last_beacon);
|
||||
|
||||
if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da))
|
||||
return;
|
||||
|
|
|
@ -256,8 +256,27 @@ static void ieee80211_restart_work(struct work_struct *work)
|
|||
|
||||
flush_work(&local->radar_detected_work);
|
||||
rtnl_lock();
|
||||
list_for_each_entry(sdata, &local->interfaces, list)
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
/*
|
||||
* XXX: there may be more work for other vif types and even
|
||||
* for station mode: a good thing would be to run most of
|
||||
* the iface type's dependent _stop (ieee80211_mg_stop,
|
||||
* ieee80211_ibss_stop) etc...
|
||||
* For now, fix only the specific bug that was seen: race
|
||||
* between csa_connection_drop_work and us.
|
||||
*/
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
/*
|
||||
* This worker is scheduled from the iface worker that
|
||||
* runs on mac80211's workqueue, so we can't be
|
||||
* scheduling this worker after the cancel right here.
|
||||
* The exception is ieee80211_chswitch_done.
|
||||
* Then we can have a race...
|
||||
*/
|
||||
cancel_work_sync(&sdata->u.mgd.csa_connection_drop_work);
|
||||
}
|
||||
flush_delayed_work(&sdata->dec_tailroom_needed_wk);
|
||||
}
|
||||
ieee80211_scan_cancel(local);
|
||||
|
||||
/* make sure any new ROC will consider local->in_reconfig */
|
||||
|
@ -471,10 +490,7 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
|
|||
cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC |
|
||||
IEEE80211_VHT_CAP_SHORT_GI_80 |
|
||||
IEEE80211_VHT_CAP_SHORT_GI_160 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_1 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_2 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_3 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_4 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_MASK |
|
||||
IEEE80211_VHT_CAP_TXSTBC |
|
||||
IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
|
||||
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||
|
@ -1208,6 +1224,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
|||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
unregister_inet6addr_notifier(&local->ifa6_notifier);
|
||||
#endif
|
||||
ieee80211_txq_teardown_flows(local);
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
|
@ -1236,7 +1253,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
|||
skb_queue_purge(&local->skb_queue);
|
||||
skb_queue_purge(&local->skb_queue_unreliable);
|
||||
skb_queue_purge(&local->skb_queue_tdls_chsw);
|
||||
ieee80211_txq_teardown_flows(local);
|
||||
|
||||
destroy_workqueue(local->workqueue);
|
||||
wiphy_unregister(local->hw.wiphy);
|
||||
|
|
|
@ -572,6 +572,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
|
|||
forward = false;
|
||||
reply = true;
|
||||
target_metric = 0;
|
||||
|
||||
if (SN_GT(target_sn, ifmsh->sn))
|
||||
ifmsh->sn = target_sn;
|
||||
|
||||
if (time_after(jiffies, ifmsh->last_sn_update +
|
||||
net_traversal_jiffies(sdata)) ||
|
||||
time_before(jiffies, ifmsh->last_sn_update)) {
|
||||
|
|
|
@ -1073,6 +1073,10 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
|||
*/
|
||||
|
||||
if (sdata->reserved_chanctx) {
|
||||
struct ieee80211_supported_band *sband = NULL;
|
||||
struct sta_info *mgd_sta = NULL;
|
||||
enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_20;
|
||||
|
||||
/*
|
||||
* with multi-vif csa driver may call ieee80211_csa_finish()
|
||||
* many times while waiting for other interfaces to use their
|
||||
|
@ -1081,6 +1085,48 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
|||
if (sdata->reserved_ready)
|
||||
goto out;
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width !=
|
||||
sdata->csa_chandef.width) {
|
||||
/*
|
||||
* For managed interface, we need to also update the AP
|
||||
* station bandwidth and align the rate scale algorithm
|
||||
* on the bandwidth change. Here we only consider the
|
||||
* bandwidth of the new channel definition (as channel
|
||||
* switch flow does not have the full HT/VHT/HE
|
||||
* information), assuming that if additional changes are
|
||||
* required they would be done as part of the processing
|
||||
* of the next beacon from the AP.
|
||||
*/
|
||||
switch (sdata->csa_chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
default:
|
||||
bw = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
bw = IEEE80211_STA_RX_BW_40;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
bw = IEEE80211_STA_RX_BW_80;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
bw = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
|
||||
mgd_sta = sta_info_get(sdata, ifmgd->bssid);
|
||||
sband =
|
||||
local->hw.wiphy->bands[sdata->csa_chandef.chan->band];
|
||||
}
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width >
|
||||
sdata->csa_chandef.width) {
|
||||
mgd_sta->sta.bandwidth = bw;
|
||||
rate_control_rate_update(local, sband, mgd_sta,
|
||||
IEEE80211_RC_BW_CHANGED);
|
||||
}
|
||||
|
||||
ret = ieee80211_vif_use_reserved_context(sdata);
|
||||
if (ret) {
|
||||
sdata_info(sdata,
|
||||
|
@ -1091,6 +1137,13 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width <
|
||||
sdata->csa_chandef.width) {
|
||||
mgd_sta->sta.bandwidth = bw;
|
||||
rate_control_rate_update(local, sband, mgd_sta,
|
||||
IEEE80211_RC_BW_CHANGED);
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1312,6 +1365,16 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
cbss->beacon_interval));
|
||||
return;
|
||||
drop_connection:
|
||||
/*
|
||||
* This is just so that the disconnect flow will know that
|
||||
* we were trying to switch channel and failed. In case the
|
||||
* mode is 1 (we are not allowed to Tx), we will know not to
|
||||
* send a deauthentication frame. Those two fields will be
|
||||
* reset when the disconnection worker runs.
|
||||
*/
|
||||
sdata->vif.csa_active = true;
|
||||
sdata->csa_block_tx = csa_ie.mode;
|
||||
|
||||
ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
|
@ -2522,6 +2585,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
bool tx;
|
||||
|
||||
sdata_lock(sdata);
|
||||
if (!ifmgd->associated) {
|
||||
|
@ -2529,6 +2593,8 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|||
return;
|
||||
}
|
||||
|
||||
tx = !sdata->csa_block_tx;
|
||||
|
||||
/* AP is probably out of range (or not reachable for another reason) so
|
||||
* remove the bss struct for that AP.
|
||||
*/
|
||||
|
@ -2536,7 +2602,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
true, frame_buf);
|
||||
tx, frame_buf);
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->csa_waiting_bcn = false;
|
||||
|
@ -2547,7 +2613,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
|
||||
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
|
||||
|
||||
sdata_unlock(sdata);
|
||||
|
|
|
@ -1728,6 +1728,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
|||
*/
|
||||
if (!ieee80211_hw_check(&sta->local->hw, AP_LINK_PS) &&
|
||||
!ieee80211_has_morefrags(hdr->frame_control) &&
|
||||
!is_multicast_ether_addr(hdr->addr1) &&
|
||||
(ieee80211_is_mgmt(hdr->frame_control) ||
|
||||
ieee80211_is_data(hdr->frame_control)) &&
|
||||
!(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
|
||||
|
|
|
@ -3078,27 +3078,18 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta)
|
|||
}
|
||||
|
||||
static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
|
||||
struct sk_buff *skb, int headroom,
|
||||
int *subframe_len)
|
||||
struct sk_buff *skb, int headroom)
|
||||
{
|
||||
int amsdu_len = *subframe_len + sizeof(struct ethhdr);
|
||||
int padding = (4 - amsdu_len) & 3;
|
||||
|
||||
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
|
||||
if (skb_headroom(skb) < headroom) {
|
||||
I802_DEBUG_INC(local->tx_expand_skb_head);
|
||||
|
||||
if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
|
||||
if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
|
||||
wiphy_debug(local->hw.wiphy,
|
||||
"failed to reallocate TX buffer\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (padding) {
|
||||
*subframe_len += padding;
|
||||
skb_put_zero(skb, padding);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3122,8 +3113,7 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
|
|||
if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
|
||||
return true;
|
||||
|
||||
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr),
|
||||
&subframe_len))
|
||||
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr)))
|
||||
return false;
|
||||
|
||||
data = skb_push(skb, sizeof(*amsdu_hdr));
|
||||
|
@ -3189,7 +3179,8 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
|||
void *data;
|
||||
bool ret = false;
|
||||
unsigned int orig_len;
|
||||
int n = 1, nfrags;
|
||||
int n = 2, nfrags, pad = 0;
|
||||
u16 hdrlen;
|
||||
|
||||
if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
|
||||
return false;
|
||||
|
@ -3222,9 +3213,6 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
|||
if (skb->len + head->len > max_amsdu_len)
|
||||
goto out;
|
||||
|
||||
if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
|
||||
goto out;
|
||||
|
||||
nfrags = 1 + skb_shinfo(skb)->nr_frags;
|
||||
nfrags += 1 + skb_shinfo(head)->nr_frags;
|
||||
frag_tail = &skb_shinfo(head)->frag_list;
|
||||
|
@ -3240,10 +3228,24 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
|||
if (max_frags && nfrags > max_frags)
|
||||
goto out;
|
||||
|
||||
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
|
||||
&subframe_len))
|
||||
if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Pad out the previous subframe to a multiple of 4 by adding the
|
||||
* padding to the next one, that's being added. Note that head->len
|
||||
* is the length of the full A-MSDU, but that works since each time
|
||||
* we add a new subframe we pad out the previous one to a multiple
|
||||
* of 4 and thus it no longer matters in the next round.
|
||||
*/
|
||||
hdrlen = fast_tx->hdr_len - sizeof(rfc1042_header);
|
||||
if ((head->len - hdrlen) & 3)
|
||||
pad = 4 - ((head->len - hdrlen) & 3);
|
||||
|
||||
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) +
|
||||
2 + pad))
|
||||
goto out_recalc;
|
||||
|
||||
ret = true;
|
||||
data = skb_push(skb, ETH_ALEN + 2);
|
||||
memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
|
||||
|
@ -3253,15 +3255,19 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
|||
memcpy(data, &len, 2);
|
||||
memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
|
||||
|
||||
memset(skb_push(skb, pad), 0, pad);
|
||||
|
||||
head->len += skb->len;
|
||||
head->data_len += skb->len;
|
||||
*frag_tail = skb;
|
||||
|
||||
flow->backlog += head->len - orig_len;
|
||||
tin->backlog_bytes += head->len - orig_len;
|
||||
|
||||
fq_recalc_backlog(fq, tin, flow);
|
||||
out_recalc:
|
||||
if (head->len != orig_len) {
|
||||
flow->backlog += head->len - orig_len;
|
||||
tin->backlog_bytes += head->len - orig_len;
|
||||
|
||||
fq_recalc_backlog(fq, tin, flow);
|
||||
}
|
||||
out:
|
||||
spin_unlock_bh(&fq->lock);
|
||||
|
||||
|
|
|
@ -1135,7 +1135,7 @@ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
|
|||
{
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
const struct ieee80211_reg_rule *rrule;
|
||||
struct ieee80211_wmm_ac *wmm_ac;
|
||||
const struct ieee80211_wmm_ac *wmm_ac;
|
||||
u16 center_freq = 0;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||
|
@ -1154,20 +1154,19 @@ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq));
|
||||
|
||||
if (IS_ERR_OR_NULL(rrule) || !rrule->wmm_rule) {
|
||||
if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
wmm_ac = &rrule->wmm_rule->ap[ac];
|
||||
wmm_ac = &rrule->wmm_rule.ap[ac];
|
||||
else
|
||||
wmm_ac = &rrule->wmm_rule->client[ac];
|
||||
wmm_ac = &rrule->wmm_rule.client[ac];
|
||||
qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min);
|
||||
qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max);
|
||||
qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn);
|
||||
qparam->txop = !qparam->txop ? wmm_ac->cot / 32 :
|
||||
min_t(u16, qparam->txop, wmm_ac->cot / 32);
|
||||
qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
|
|
@ -669,13 +669,13 @@ static int nl80211_msg_put_wmm_rules(struct sk_buff *msg,
|
|||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u16(msg, NL80211_WMMR_CW_MIN,
|
||||
rule->wmm_rule->client[j].cw_min) ||
|
||||
rule->wmm_rule.client[j].cw_min) ||
|
||||
nla_put_u16(msg, NL80211_WMMR_CW_MAX,
|
||||
rule->wmm_rule->client[j].cw_max) ||
|
||||
rule->wmm_rule.client[j].cw_max) ||
|
||||
nla_put_u8(msg, NL80211_WMMR_AIFSN,
|
||||
rule->wmm_rule->client[j].aifsn) ||
|
||||
nla_put_u8(msg, NL80211_WMMR_TXOP,
|
||||
rule->wmm_rule->client[j].cot))
|
||||
rule->wmm_rule.client[j].aifsn) ||
|
||||
nla_put_u16(msg, NL80211_WMMR_TXOP,
|
||||
rule->wmm_rule.client[j].cot))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, nl_wmm_rule);
|
||||
|
@ -766,9 +766,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
|
|||
|
||||
if (large) {
|
||||
const struct ieee80211_reg_rule *rule =
|
||||
freq_reg_info(wiphy, chan->center_freq);
|
||||
freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
|
||||
|
||||
if (!IS_ERR(rule) && rule->wmm_rule) {
|
||||
if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) {
|
||||
if (nl80211_msg_put_wmm_rules(msg, rule))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
@ -12205,6 +12205,7 @@ static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EOPNOTSUPP;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_MDID] ||
|
||||
!info->attrs[NL80211_ATTR_IE] ||
|
||||
!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -425,36 +425,23 @@ static const struct ieee80211_regdomain *
|
|||
reg_copy_regd(const struct ieee80211_regdomain *src_regd)
|
||||
{
|
||||
struct ieee80211_regdomain *regd;
|
||||
int size_of_regd, size_of_wmms;
|
||||
int size_of_regd;
|
||||
unsigned int i;
|
||||
struct ieee80211_wmm_rule *d_wmm, *s_wmm;
|
||||
|
||||
size_of_regd =
|
||||
sizeof(struct ieee80211_regdomain) +
|
||||
src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
|
||||
size_of_wmms = src_regd->n_wmm_rules *
|
||||
sizeof(struct ieee80211_wmm_rule);
|
||||
|
||||
regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL);
|
||||
regd = kzalloc(size_of_regd, GFP_KERNEL);
|
||||
if (!regd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
|
||||
|
||||
d_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
|
||||
s_wmm = (struct ieee80211_wmm_rule *)((u8 *)src_regd + size_of_regd);
|
||||
memcpy(d_wmm, s_wmm, size_of_wmms);
|
||||
|
||||
for (i = 0; i < src_regd->n_reg_rules; i++) {
|
||||
for (i = 0; i < src_regd->n_reg_rules; i++)
|
||||
memcpy(®d->reg_rules[i], &src_regd->reg_rules[i],
|
||||
sizeof(struct ieee80211_reg_rule));
|
||||
if (!src_regd->reg_rules[i].wmm_rule)
|
||||
continue;
|
||||
|
||||
regd->reg_rules[i].wmm_rule = d_wmm +
|
||||
(src_regd->reg_rules[i].wmm_rule - s_wmm) /
|
||||
sizeof(struct ieee80211_wmm_rule);
|
||||
}
|
||||
return regd;
|
||||
}
|
||||
|
||||
|
@ -860,9 +847,10 @@ static bool valid_regdb(const u8 *data, unsigned int size)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void set_wmm_rule(struct ieee80211_wmm_rule *rule,
|
||||
static void set_wmm_rule(struct ieee80211_reg_rule *rrule,
|
||||
struct fwdb_wmm_rule *wmm)
|
||||
{
|
||||
struct ieee80211_wmm_rule *rule = &rrule->wmm_rule;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
|
@ -876,11 +864,13 @@ static void set_wmm_rule(struct ieee80211_wmm_rule *rule,
|
|||
rule->ap[i].aifsn = wmm->ap[i].aifsn;
|
||||
rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot);
|
||||
}
|
||||
|
||||
rrule->has_wmm = true;
|
||||
}
|
||||
|
||||
static int __regdb_query_wmm(const struct fwdb_header *db,
|
||||
const struct fwdb_country *country, int freq,
|
||||
u32 *dbptr, struct ieee80211_wmm_rule *rule)
|
||||
struct ieee80211_reg_rule *rule)
|
||||
{
|
||||
unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2;
|
||||
struct fwdb_collection *coll = (void *)((u8 *)db + ptr);
|
||||
|
@ -901,8 +891,6 @@ static int __regdb_query_wmm(const struct fwdb_header *db,
|
|||
wmm_ptr = be16_to_cpu(rrule->wmm_ptr) << 2;
|
||||
wmm = (void *)((u8 *)db + wmm_ptr);
|
||||
set_wmm_rule(rule, wmm);
|
||||
if (dbptr)
|
||||
*dbptr = wmm_ptr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -910,8 +898,7 @@ static int __regdb_query_wmm(const struct fwdb_header *db,
|
|||
return -ENODATA;
|
||||
}
|
||||
|
||||
int reg_query_regdb_wmm(char *alpha2, int freq, u32 *dbptr,
|
||||
struct ieee80211_wmm_rule *rule)
|
||||
int reg_query_regdb_wmm(char *alpha2, int freq, struct ieee80211_reg_rule *rule)
|
||||
{
|
||||
const struct fwdb_header *hdr = regdb;
|
||||
const struct fwdb_country *country;
|
||||
|
@ -925,8 +912,7 @@ int reg_query_regdb_wmm(char *alpha2, int freq, u32 *dbptr,
|
|||
country = &hdr->country[0];
|
||||
while (country->coll_ptr) {
|
||||
if (alpha2_equal(alpha2, country->alpha2))
|
||||
return __regdb_query_wmm(regdb, country, freq, dbptr,
|
||||
rule);
|
||||
return __regdb_query_wmm(regdb, country, freq, rule);
|
||||
|
||||
country++;
|
||||
}
|
||||
|
@ -935,32 +921,13 @@ int reg_query_regdb_wmm(char *alpha2, int freq, u32 *dbptr,
|
|||
}
|
||||
EXPORT_SYMBOL(reg_query_regdb_wmm);
|
||||
|
||||
struct wmm_ptrs {
|
||||
struct ieee80211_wmm_rule *rule;
|
||||
u32 ptr;
|
||||
};
|
||||
|
||||
static struct ieee80211_wmm_rule *find_wmm_ptr(struct wmm_ptrs *wmm_ptrs,
|
||||
u32 wmm_ptr, int n_wmms)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_wmms; i++) {
|
||||
if (wmm_ptrs[i].ptr == wmm_ptr)
|
||||
return wmm_ptrs[i].rule;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int regdb_query_country(const struct fwdb_header *db,
|
||||
const struct fwdb_country *country)
|
||||
{
|
||||
unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2;
|
||||
struct fwdb_collection *coll = (void *)((u8 *)db + ptr);
|
||||
struct ieee80211_regdomain *regdom;
|
||||
struct ieee80211_regdomain *tmp_rd;
|
||||
unsigned int size_of_regd, i, n_wmms = 0;
|
||||
struct wmm_ptrs *wmm_ptrs;
|
||||
unsigned int size_of_regd, i;
|
||||
|
||||
size_of_regd = sizeof(struct ieee80211_regdomain) +
|
||||
coll->n_rules * sizeof(struct ieee80211_reg_rule);
|
||||
|
@ -969,12 +936,6 @@ static int regdb_query_country(const struct fwdb_header *db,
|
|||
if (!regdom)
|
||||
return -ENOMEM;
|
||||
|
||||
wmm_ptrs = kcalloc(coll->n_rules, sizeof(*wmm_ptrs), GFP_KERNEL);
|
||||
if (!wmm_ptrs) {
|
||||
kfree(regdom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
regdom->n_reg_rules = coll->n_rules;
|
||||
regdom->alpha2[0] = country->alpha2[0];
|
||||
regdom->alpha2[1] = country->alpha2[1];
|
||||
|
@ -1013,37 +974,11 @@ static int regdb_query_country(const struct fwdb_header *db,
|
|||
1000 * be16_to_cpu(rule->cac_timeout);
|
||||
if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) {
|
||||
u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
|
||||
struct ieee80211_wmm_rule *wmm_pos =
|
||||
find_wmm_ptr(wmm_ptrs, wmm_ptr, n_wmms);
|
||||
struct fwdb_wmm_rule *wmm;
|
||||
struct ieee80211_wmm_rule *wmm_rule;
|
||||
struct fwdb_wmm_rule *wmm = (void *)((u8 *)db + wmm_ptr);
|
||||
|
||||
if (wmm_pos) {
|
||||
rrule->wmm_rule = wmm_pos;
|
||||
continue;
|
||||
}
|
||||
wmm = (void *)((u8 *)db + wmm_ptr);
|
||||
tmp_rd = krealloc(regdom, size_of_regd + (n_wmms + 1) *
|
||||
sizeof(struct ieee80211_wmm_rule),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!tmp_rd) {
|
||||
kfree(regdom);
|
||||
kfree(wmm_ptrs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
regdom = tmp_rd;
|
||||
|
||||
wmm_rule = (struct ieee80211_wmm_rule *)
|
||||
((u8 *)regdom + size_of_regd + n_wmms *
|
||||
sizeof(struct ieee80211_wmm_rule));
|
||||
|
||||
set_wmm_rule(wmm_rule, wmm);
|
||||
wmm_ptrs[n_wmms].ptr = wmm_ptr;
|
||||
wmm_ptrs[n_wmms++].rule = wmm_rule;
|
||||
set_wmm_rule(rrule, wmm);
|
||||
}
|
||||
}
|
||||
kfree(wmm_ptrs);
|
||||
|
||||
return reg_schedule_apply(regdom);
|
||||
}
|
||||
|
|
|
@ -1456,7 +1456,7 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
|
|||
u8 *op_class)
|
||||
{
|
||||
u8 vht_opclass;
|
||||
u16 freq = chandef->center_freq1;
|
||||
u32 freq = chandef->center_freq1;
|
||||
|
||||
if (freq >= 2412 && freq <= 2472) {
|
||||
if (chandef->width > NL80211_CHAN_WIDTH_40)
|
||||
|
|
Loading…
Reference in New Issue