wireless-drivers fixes for 5.3

Second set of fixes for 5.3. Lots of iwlwifi fixes have accumulated
 which consists most of patches in this pull request. Only most notable
 iwlwifi fixes are listed below.
 
 mwifiex
 
 * fix a regression related to WPA1 networks since v5.3-rc1
 
 iwlwifi
 
 * fix use-after-free issues
 
 * fix DMA mapping API usage errors
 
 * fix frame drop occurring due to reorder buffer handling in
   RSS in certain conditions
 
 * fix rate scale locking issues
 
 * disable TX A-MSDU on older NICs as it causes problems and was
   never supposed to be supported
 
 * new PCI IDs
 
 * GEO_TX_POWER_LIMIT API issue that many people were hitting
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJdSWJ0AAoJEG4XJFUm622bwb4H/1cbH/VoPkNPfRb6783G3Fuc
 SWcI655nMDHY3cElhLPGQrR4OuNYbFpBDT7O73XMIVqnkHlgq3L/VZD2qaacSd4H
 tY4HEkkHobil9gdsEt8hpBstp9xNi36klDiq9gEOLT88eatdukCZTsCnBwaTFSEQ
 84QxsVsHWuB+khVDay6jprA+vwL6YopOlPwfY9+q0LHWCHq1C98ZqPZoxjb8dzN7
 bbW4hWIhMHRtN9ixPwvoRhscev2ZgRPHAncoGFE8bmRM79srZpG0mDeAhQPLGrJz
 YSoGtylHpQxg3vzz2y80fMpH6jsUHFeA56xuKPQ5gnUm4bPeFbhx7h29DuABsAw=
 =GDUP
 -----END PGP SIGNATURE-----

Merge tag 'wireless-drivers-for-davem-2019-08-06' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers

Kalle Valo says:

====================
wireless-drivers fixes for 5.3

Second set of fixes for 5.3. Lots of iwlwifi fixes have accumulated
which consists most of patches in this pull request. Only most notable
iwlwifi fixes are listed below.

mwifiex

* fix a regression related to WPA1 networks since v5.3-rc1

iwlwifi

* fix use-after-free issues

* fix DMA mapping API usage errors

* fix frame drop occurring due to reorder buffer handling in
  RSS in certain conditions

* fix rate scale locking issues

* disable TX A-MSDU on older NICs as it causes problems and was
  never supposed to be supported

* new PCI IDs

* GEO_TX_POWER_LIMIT API issue that many people were hitting
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-08-06 11:48:48 -07:00
commit 0574f2ed30
19 changed files with 538 additions and 353 deletions

View File

@ -776,7 +776,6 @@ struct iwl_rss_config_cmd {
u8 indirection_table[IWL_RSS_INDIRECTION_TABLE_SIZE];
} __packed; /* RSS_CONFIG_CMD_API_S_VER_1 */
#define IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE 128
#define IWL_MULTI_QUEUE_SYNC_SENDER_POS 0
#define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf
@ -812,10 +811,12 @@ struct iwl_rxq_sync_notification {
*
* @IWL_MVM_RXQ_EMPTY: empty sync notification
* @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA
* @IWL_MVM_RXQ_NSSN_SYNC: notify all the RSS queues with the new NSSN
*/
enum iwl_mvm_rxq_notif_type {
IWL_MVM_RXQ_EMPTY,
IWL_MVM_RXQ_NOTIF_DEL_BA,
IWL_MVM_RXQ_NSSN_SYNC,
};
/**

View File

@ -2438,17 +2438,19 @@ static void iwl_fw_dbg_info_apply(struct iwl_fw_runtime *fwrt,
{
u32 img_name_len = le32_to_cpu(dbg_info->img_name_len);
u32 dbg_cfg_name_len = le32_to_cpu(dbg_info->dbg_cfg_name_len);
const char err_str[] =
"WRT: ext=%d. Invalid %s name length %d, expected %d\n";
if (img_name_len != IWL_FW_INI_MAX_IMG_NAME_LEN) {
IWL_WARN(fwrt, err_str, ext, "image", img_name_len,
IWL_WARN(fwrt,
"WRT: ext=%d. Invalid image name length %d, expected %d\n",
ext, img_name_len,
IWL_FW_INI_MAX_IMG_NAME_LEN);
return;
}
if (dbg_cfg_name_len != IWL_FW_INI_MAX_DBG_CFG_NAME_LEN) {
IWL_WARN(fwrt, err_str, ext, "debug cfg", dbg_cfg_name_len,
IWL_WARN(fwrt,
"WRT: ext=%d. Invalid debug cfg name length %d, expected %d\n",
ext, dbg_cfg_name_len,
IWL_FW_INI_MAX_DBG_CFG_NAME_LEN);
return;
}
@ -2775,8 +2777,6 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
struct iwl_ucode_tlv *tlv = iter;
void *ini_tlv = (void *)tlv->data;
u32 type = le32_to_cpu(tlv->type);
const char invalid_ap_str[] =
"WRT: ext=%d. Invalid apply point %d for %s\n";
switch (type) {
case IWL_UCODE_TLV_TYPE_DEBUG_INFO:
@ -2786,8 +2786,9 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_allocation_data *buf_alloc = ini_tlv;
if (pnt != IWL_FW_INI_APPLY_EARLY) {
IWL_ERR(fwrt, invalid_ap_str, ext, pnt,
"buffer allocation");
IWL_ERR(fwrt,
"WRT: ext=%d. Invalid apply point %d for buffer allocation\n",
ext, pnt);
goto next;
}
@ -2797,8 +2798,9 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
}
case IWL_UCODE_TLV_TYPE_HCMD:
if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) {
IWL_ERR(fwrt, invalid_ap_str, ext, pnt,
"host command");
IWL_ERR(fwrt,
"WRT: ext=%d. Invalid apply point %d for host command\n",
ext, pnt);
goto next;
}
iwl_fw_dbg_send_hcmd(fwrt, tlv, ext);

View File

@ -1640,6 +1640,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans)
init_completion(&drv->request_firmware_complete);
INIT_LIST_HEAD(&drv->list);
iwl_load_fw_dbg_tlv(drv->trans->dev, drv->trans);
#ifdef CONFIG_IWLWIFI_DEBUGFS
/* Create the device debugfs entries. */
drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev),
@ -1660,8 +1662,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans)
err_fw:
#ifdef CONFIG_IWLWIFI_DEBUGFS
debugfs_remove_recursive(drv->dbgfs_drv);
iwl_fw_dbg_free(drv->trans);
#endif
iwl_fw_dbg_free(drv->trans);
kfree(drv);
err:
return ERR_PTR(ret);

View File

@ -755,7 +755,7 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm)
for (i = 0; i < n_profiles; i++) {
/* the tables start at element 3 */
static int pos = 3;
int pos = 3;
/* The EWRD profiles officially go from 2 to 4, but we
* save them in sar_profiles[1-3] (because we don't
@ -880,6 +880,22 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b)
return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd);
}
static bool iwl_mvm_sar_geo_support(struct iwl_mvm *mvm)
{
/*
* The GEO_TX_POWER_LIMIT command is not supported on earlier
* firmware versions. Unfortunately, we don't have a TLV API
* flag to rely on, so rely on the major version which is in
* the first byte of ucode_ver. This was implemented
* initially on version 38 and then backported to 36, 29 and
* 17.
*/
return IWL_UCODE_SERIAL(mvm->fw->ucode_ver) >= 38 ||
IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 36 ||
IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 29 ||
IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 17;
}
int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
{
struct iwl_geo_tx_power_profiles_resp *resp;
@ -909,6 +925,9 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
.data = { data },
};
if (!iwl_mvm_sar_geo_support(mvm))
return -EOPNOTSUPP;
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret) {
IWL_ERR(mvm, "Failed to get geographic profile info %d\n", ret);
@ -934,13 +953,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
int ret, i, j;
u16 cmd_wide_id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT);
/*
* This command is not supported on earlier firmware versions.
* Unfortunately, we don't have a TLV API flag to rely on, so
* rely on the major version which is in the first byte of
* ucode_ver.
*/
if (IWL_UCODE_SERIAL(mvm->fw->ucode_ver) < 41)
if (!iwl_mvm_sar_geo_support(mvm))
return 0;
ret = iwl_mvm_sar_get_wgds_table(mvm);

View File

@ -207,11 +207,11 @@ static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = {
},
};
static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);
static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
{
@ -474,7 +474,19 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
ieee80211_hw_set(hw, BUFF_MMPDU_TXQ);
ieee80211_hw_set(hw, STA_MMPDU_TXQ);
ieee80211_hw_set(hw, TX_AMSDU);
/*
* On older devices, enabling TX A-MSDU occasionally leads to
* something getting messed up, the command read from the FIFO
* gets out of sync and isn't a TX command, so that we have an
* assert EDC.
*
* It's not clear where the bug is, but since we didn't used to
* support A-MSDU until moving the mac80211 iTXQs, just leave it
* for older devices. We also don't see this issue on any newer
* devices.
*/
if (mvm->cfg->device_family >= IWL_DEVICE_FAMILY_9000)
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, TX_FRAG_LIST);
if (iwl_mvm_has_tlc_offload(mvm)) {
@ -2726,7 +2738,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
mvmvif->ap_early_keys[i] = NULL;
ret = iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key);
ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key);
if (ret)
goto out_quota_failed;
}
@ -3494,11 +3506,11 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
return ret;
}
static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
@ -3553,8 +3565,6 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
return -EOPNOTSUPP;
}
mutex_lock(&mvm->mutex);
switch (cmd) {
case SET_KEY:
if ((vif->type == NL80211_IFTYPE_ADHOC ||
@ -3700,7 +3710,22 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
ret = -EINVAL;
}
return ret;
}
static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
mutex_lock(&mvm->mutex);
ret = __iwl_mvm_mac_set_key(hw, cmd, vif, sta, key);
mutex_unlock(&mvm->mutex);
return ret;
}
@ -5041,7 +5066,6 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
u32 qmask = BIT(mvm->trans->num_rx_queues) - 1;
int ret;
lockdep_assert_held(&mvm->mutex);
if (!iwl_mvm_has_new_rx_api(mvm))
return;
@ -5052,13 +5076,15 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
atomic_set(&mvm->queue_sync_counter,
mvm->trans->num_rx_queues);
ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif, size);
ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif,
size, !notif->sync);
if (ret) {
IWL_ERR(mvm, "Failed to trigger RX queues sync (%d)\n", ret);
goto out;
}
if (notif->sync) {
lockdep_assert_held(&mvm->mutex);
ret = wait_event_timeout(mvm->rx_sync_waitq,
atomic_read(&mvm->queue_sync_counter) == 0 ||
iwl_mvm_is_radio_killed(mvm),

View File

@ -1664,9 +1664,9 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, int queue);
int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
const u8 *data, u32 count);
void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
int queue);
const u8 *data, u32 count, bool async);
void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, int queue);
void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
@ -1813,7 +1813,7 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
#endif /* CONFIG_IWLWIFI_DEBUGFS */
/* rate scaling */
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool sync);
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq);
void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg);
int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate);
void rs_update_last_rssi(struct iwl_mvm *mvm,

View File

@ -620,7 +620,7 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
enum iwl_mcc_source src;
char mcc[3];
struct ieee80211_regdomain *regd;
u32 wgds_tbl_idx;
int wgds_tbl_idx;
lockdep_assert_held(&mvm->mutex);

View File

@ -1088,7 +1088,7 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0);
else if (unlikely(cmd == WIDE_ID(DATA_PATH_GROUP,
RX_QUEUES_NOTIFICATION)))
iwl_mvm_rx_queue_notif(mvm, rxb, 0);
iwl_mvm_rx_queue_notif(mvm, napi, rxb, 0);
else if (cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))
iwl_mvm_rx_frame_release(mvm, napi, rxb, 0);
else if (cmd == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF))
@ -1812,7 +1812,7 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode,
iwl_mvm_rx_frame_release(mvm, napi, rxb, queue);
else if (unlikely(cmd == WIDE_ID(DATA_PATH_GROUP,
RX_QUEUES_NOTIFICATION)))
iwl_mvm_rx_queue_notif(mvm, rxb, queue);
iwl_mvm_rx_queue_notif(mvm, napi, rxb, queue);
else if (likely(cmd == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))
iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue);
}

View File

@ -1197,239 +1197,6 @@ static u8 rs_get_tid(struct ieee80211_hdr *hdr)
return tid;
}
void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, struct ieee80211_tx_info *info, bool ndp)
{
int legacy_success;
int retries;
int i;
struct iwl_lq_cmd *table;
u32 lq_hwrate;
struct rs_rate lq_rate, tx_resp_rate;
struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
u32 tlc_info = (uintptr_t)info->status.status_driver_data[0];
u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK;
u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info);
u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
/* Treat uninitialized rate scaling data same as non-existing. */
if (!lq_sta) {
IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
return;
} else if (!lq_sta->pers.drv) {
IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
return;
}
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU))
return;
if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band,
&tx_resp_rate)) {
WARN_ON_ONCE(1);
return;
}
#ifdef CONFIG_MAC80211_DEBUGFS
/* Disable last tx check if we are debugging with fixed rate but
* update tx stats */
if (lq_sta->pers.dbg_fixed_rate) {
int index = tx_resp_rate.index;
enum rs_column column;
int attempts, success;
column = rs_get_column_from_rate(&tx_resp_rate);
if (WARN_ONCE(column == RS_COLUMN_INVALID,
"Can't map rate 0x%x to column",
tx_resp_hwrate))
return;
if (info->flags & IEEE80211_TX_STAT_AMPDU) {
attempts = info->status.ampdu_len;
success = info->status.ampdu_ack_len;
} else {
attempts = info->status.rates[0].count;
success = !!(info->flags & IEEE80211_TX_STAT_ACK);
}
lq_sta->pers.tx_stats[column][index].total += attempts;
lq_sta->pers.tx_stats[column][index].success += success;
IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
tx_resp_hwrate, success, attempts);
return;
}
#endif
if (time_after(jiffies,
(unsigned long)(lq_sta->last_tx +
(IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
iwl_mvm_rs_rate_init(mvm, sta, info->band, true);
return;
}
lq_sta->last_tx = jiffies;
/* Ignore this Tx frame response if its initial rate doesn't match
* that of latest Link Quality command. There may be stragglers
* from a previous Link Quality command, but we're no longer interested
* in those; they're either from the "active" mode while we're trying
* to check "search" mode, or a prior "search" mode after we've moved
* to a new "search" mode (which might become the new "active" mode).
*/
table = &lq_sta->lq;
lq_hwrate = le32_to_cpu(table->rs_table[0]);
if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) {
WARN_ON_ONCE(1);
return;
}
/* Here we actually compare this rate to the latest LQ command */
if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) {
IWL_DEBUG_RATE(mvm,
"tx resp color 0x%x does not match 0x%x\n",
lq_color, LQ_FLAG_COLOR_GET(table->flags));
/*
* Since rates mis-match, the last LQ command may have failed.
* After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
* ... driver.
*/
lq_sta->missed_rate_counter++;
if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) {
lq_sta->missed_rate_counter = 0;
IWL_DEBUG_RATE(mvm,
"Too many rates mismatch. Send sync LQ. rs_state %d\n",
lq_sta->rs_state);
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
}
/* Regardless, ignore this status info for outdated rate */
return;
} else
/* Rate did match, so reset the missed_rate_counter */
lq_sta->missed_rate_counter = 0;
if (!lq_sta->search_better_tbl) {
curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
} else {
curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
}
if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
IWL_DEBUG_RATE(mvm,
"Neither active nor search matches tx rate\n");
tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
rs_dump_rate(mvm, &lq_rate, "ACTUAL");
/*
* no matching table found, let's by-pass the data collection
* and continue to perform rate scale to find the rate table
*/
rs_stay_in_table(lq_sta, true);
goto done;
}
/*
* Updating the frame history depends on whether packets were
* aggregated.
*
* For aggregation, all packets were transmitted at the same rate, the
* first index into rate scale table.
*/
if (info->flags & IEEE80211_TX_STAT_AMPDU) {
rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index,
info->status.ampdu_len,
info->status.ampdu_ack_len,
reduced_txp);
/* ampdu_ack_len = 0 marks no BA was received. For TLC, treat
* it as a single frame loss as we don't want the success ratio
* to dip too quickly because a BA wasn't received.
* For TPC, there's no need for this optimisation since we want
* to recover very quickly from a bad power reduction and,
* therefore we'd like the success ratio to get an immediate hit
* when failing to get a BA, so we'd switch back to a lower or
* zero power reduction. When FW transmits agg with a rate
* different from the initial rate, it will not use reduced txp
* and will send BA notification twice (one empty with reduced
* txp equal to the value from LQ and one with reduced txp 0).
* We need to update counters for each txp level accordingly.
*/
if (info->status.ampdu_ack_len == 0)
info->status.ampdu_len = 1;
rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl, tx_resp_rate.index,
info->status.ampdu_len,
info->status.ampdu_ack_len);
/* Update success/fail counts if not searching for new mode */
if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
lq_sta->total_success += info->status.ampdu_ack_len;
lq_sta->total_failed += (info->status.ampdu_len -
info->status.ampdu_ack_len);
}
} else {
/* For legacy, update frame history with for each Tx retry. */
retries = info->status.rates[0].count - 1;
/* HW doesn't send more than 15 retries */
retries = min(retries, 15);
/* The last transmission may have been successful */
legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
/* Collect data for each rate used during failed TX attempts */
for (i = 0; i <= retries; ++i) {
lq_hwrate = le32_to_cpu(table->rs_table[i]);
if (rs_rate_from_ucode_rate(lq_hwrate, info->band,
&lq_rate)) {
WARN_ON_ONCE(1);
return;
}
/*
* Only collect stats if retried rate is in the same RS
* table as active/search.
*/
if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
tmp_tbl = curr_tbl;
else if (rs_rate_column_match(&lq_rate,
&other_tbl->rate))
tmp_tbl = other_tbl;
else
continue;
rs_collect_tpc_data(mvm, lq_sta, tmp_tbl,
tx_resp_rate.index, 1,
i < retries ? 0 : legacy_success,
reduced_txp);
rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl,
tx_resp_rate.index, 1,
i < retries ? 0 : legacy_success);
}
/* Update success/fail counts if not searching for new mode */
if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
lq_sta->total_success += legacy_success;
lq_sta->total_failed += retries + (1 - legacy_success);
}
}
/* The last TX rate is cached in lq_sta; it's set in if/else above */
lq_sta->last_rate_n_flags = lq_hwrate;
IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
done:
/* See if there's a better rate or modulation mode to try. */
if (sta->supp_rates[info->band])
rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp);
}
/*
* mac80211 sends us Tx status
*/
@ -1442,8 +1209,9 @@ static void rs_drv_mac80211_tx_status(void *mvm_r,
struct iwl_op_mode *op_mode = mvm_r;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
if (!iwl_mvm_sta_from_mac80211(sta)->vif)
if (!mvmsta->vif)
return;
if (!ieee80211_is_data(hdr->frame_control) ||
@ -1584,6 +1352,18 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
}
/* rs uses two tables, one is active and the second is for searching better
* configuration. This function, according to the index of the currently
* active table returns the search table, which is located at the
* index complementary to 1 according to the active table (active = 1,
* search = 0 or active = 0, search = 1).
* Since lq_info is an arary of size 2, make sure index cannot be out of bounds.
*/
static inline u8 rs_search_tbl(u8 active_tbl)
{
return (active_tbl ^ 1) & 1;
}
static s32 rs_get_best_rate(struct iwl_mvm *mvm,
struct iwl_lq_sta *lq_sta,
struct iwl_scale_tbl_info *tbl, /* "search" */
@ -1794,7 +1574,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
struct iwl_scale_tbl_info *tbl)
{
rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq);
}
static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm,
@ -1931,9 +1711,9 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
enum rs_column col_id)
{
struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
struct iwl_scale_tbl_info *search_tbl =
&(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
&lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
struct rs_rate *rate = &search_tbl->rate;
const struct rs_tx_column *column = &rs_tx_columns[col_id];
const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
@ -2341,7 +2121,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
if (!lq_sta->search_better_tbl)
active_tbl = lq_sta->active_tbl;
else
active_tbl = 1 - lq_sta->active_tbl;
active_tbl = rs_search_tbl(lq_sta->active_tbl);
tbl = &(lq_sta->lq_info[active_tbl]);
rate = &tbl->rate;
@ -2565,7 +2345,7 @@ lq_update:
/* If new "search" mode was selected, set up in uCode table */
if (lq_sta->search_better_tbl) {
/* Access the "search" table, clear its history. */
tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
rs_rate_scale_clear_tbl_windows(mvm, tbl);
/* Use new "search" start rate */
@ -2896,7 +2676,7 @@ void rs_update_last_rssi(struct iwl_mvm *mvm,
static void rs_initialize_lq(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
struct iwl_lq_sta *lq_sta,
enum nl80211_band band, bool update)
enum nl80211_band band)
{
struct iwl_scale_tbl_info *tbl;
struct rs_rate *rate;
@ -2908,7 +2688,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
if (!lq_sta->search_better_tbl)
active_tbl = lq_sta->active_tbl;
else
active_tbl = 1 - lq_sta->active_tbl;
active_tbl = rs_search_tbl(lq_sta->active_tbl);
tbl = &(lq_sta->lq_info[active_tbl]);
rate = &tbl->rate;
@ -2926,7 +2706,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
rs_set_expected_tpt_table(lq_sta, tbl);
rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
/* TODO restore station should remember the lq cmd */
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, !update);
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq);
}
static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
@ -3175,7 +2955,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
* Called after adding a new station to initialize rate scaling
*/
static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band, bool update)
enum nl80211_band band)
{
int i, j;
struct ieee80211_hw *hw = mvm->hw;
@ -3186,6 +2966,8 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband;
unsigned long supp; /* must be unsigned long for for_each_set_bit */
lockdep_assert_held(&mvmsta->lq_sta.rs_drv.pers.lock);
/* clear all non-persistent lq data */
memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
@ -3255,7 +3037,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
#ifdef CONFIG_IWLWIFI_DEBUGFS
iwl_mvm_reset_frame_stats(mvm);
#endif
rs_initialize_lq(mvm, sta, lq_sta, band, update);
rs_initialize_lq(mvm, sta, lq_sta, band);
}
static void rs_drv_rate_update(void *mvm_r,
@ -3278,6 +3060,258 @@ static void rs_drv_rate_update(void *mvm_r,
iwl_mvm_rs_rate_init(mvm, sta, sband->band, true);
}
static void __iwl_mvm_rs_tx_status(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
int tid, struct ieee80211_tx_info *info,
bool ndp)
{
int legacy_success;
int retries;
int i;
struct iwl_lq_cmd *table;
u32 lq_hwrate;
struct rs_rate lq_rate, tx_resp_rate;
struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
u32 tlc_info = (uintptr_t)info->status.status_driver_data[0];
u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK;
u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info);
u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
/* Treat uninitialized rate scaling data same as non-existing. */
if (!lq_sta) {
IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
return;
} else if (!lq_sta->pers.drv) {
IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
return;
}
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU))
return;
if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band,
&tx_resp_rate)) {
WARN_ON_ONCE(1);
return;
}
#ifdef CONFIG_MAC80211_DEBUGFS
/* Disable last tx check if we are debugging with fixed rate but
* update tx stats
*/
if (lq_sta->pers.dbg_fixed_rate) {
int index = tx_resp_rate.index;
enum rs_column column;
int attempts, success;
column = rs_get_column_from_rate(&tx_resp_rate);
if (WARN_ONCE(column == RS_COLUMN_INVALID,
"Can't map rate 0x%x to column",
tx_resp_hwrate))
return;
if (info->flags & IEEE80211_TX_STAT_AMPDU) {
attempts = info->status.ampdu_len;
success = info->status.ampdu_ack_len;
} else {
attempts = info->status.rates[0].count;
success = !!(info->flags & IEEE80211_TX_STAT_ACK);
}
lq_sta->pers.tx_stats[column][index].total += attempts;
lq_sta->pers.tx_stats[column][index].success += success;
IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
tx_resp_hwrate, success, attempts);
return;
}
#endif
if (time_after(jiffies,
(unsigned long)(lq_sta->last_tx +
(IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
/* reach here only in case of driver RS, call directly
* the unlocked version
*/
rs_drv_rate_init(mvm, sta, info->band);
return;
}
lq_sta->last_tx = jiffies;
/* Ignore this Tx frame response if its initial rate doesn't match
* that of latest Link Quality command. There may be stragglers
* from a previous Link Quality command, but we're no longer interested
* in those; they're either from the "active" mode while we're trying
* to check "search" mode, or a prior "search" mode after we've moved
* to a new "search" mode (which might become the new "active" mode).
*/
table = &lq_sta->lq;
lq_hwrate = le32_to_cpu(table->rs_table[0]);
if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) {
WARN_ON_ONCE(1);
return;
}
/* Here we actually compare this rate to the latest LQ command */
if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) {
IWL_DEBUG_RATE(mvm,
"tx resp color 0x%x does not match 0x%x\n",
lq_color, LQ_FLAG_COLOR_GET(table->flags));
/* Since rates mis-match, the last LQ command may have failed.
* After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
* ... driver.
*/
lq_sta->missed_rate_counter++;
if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) {
lq_sta->missed_rate_counter = 0;
IWL_DEBUG_RATE(mvm,
"Too many rates mismatch. Send sync LQ. rs_state %d\n",
lq_sta->rs_state);
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq);
}
/* Regardless, ignore this status info for outdated rate */
return;
}
/* Rate did match, so reset the missed_rate_counter */
lq_sta->missed_rate_counter = 0;
if (!lq_sta->search_better_tbl) {
curr_tbl = &lq_sta->lq_info[lq_sta->active_tbl];
other_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
} else {
curr_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
other_tbl = &lq_sta->lq_info[lq_sta->active_tbl];
}
if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
IWL_DEBUG_RATE(mvm,
"Neither active nor search matches tx rate\n");
tmp_tbl = &lq_sta->lq_info[lq_sta->active_tbl];
rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
tmp_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
rs_dump_rate(mvm, &lq_rate, "ACTUAL");
/* no matching table found, let's by-pass the data collection
* and continue to perform rate scale to find the rate table
*/
rs_stay_in_table(lq_sta, true);
goto done;
}
/* Updating the frame history depends on whether packets were
* aggregated.
*
* For aggregation, all packets were transmitted at the same rate, the
* first index into rate scale table.
*/
if (info->flags & IEEE80211_TX_STAT_AMPDU) {
rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index,
info->status.ampdu_len,
info->status.ampdu_ack_len,
reduced_txp);
/* ampdu_ack_len = 0 marks no BA was received. For TLC, treat
* it as a single frame loss as we don't want the success ratio
* to dip too quickly because a BA wasn't received.
* For TPC, there's no need for this optimisation since we want
* to recover very quickly from a bad power reduction and,
* therefore we'd like the success ratio to get an immediate hit
* when failing to get a BA, so we'd switch back to a lower or
* zero power reduction. When FW transmits agg with a rate
* different from the initial rate, it will not use reduced txp
* and will send BA notification twice (one empty with reduced
* txp equal to the value from LQ and one with reduced txp 0).
* We need to update counters for each txp level accordingly.
*/
if (info->status.ampdu_ack_len == 0)
info->status.ampdu_len = 1;
rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl,
tx_resp_rate.index,
info->status.ampdu_len,
info->status.ampdu_ack_len);
/* Update success/fail counts if not searching for new mode */
if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
lq_sta->total_success += info->status.ampdu_ack_len;
lq_sta->total_failed += (info->status.ampdu_len -
info->status.ampdu_ack_len);
}
} else {
/* For legacy, update frame history with for each Tx retry. */
retries = info->status.rates[0].count - 1;
/* HW doesn't send more than 15 retries */
retries = min(retries, 15);
/* The last transmission may have been successful */
legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
/* Collect data for each rate used during failed TX attempts */
for (i = 0; i <= retries; ++i) {
lq_hwrate = le32_to_cpu(table->rs_table[i]);
if (rs_rate_from_ucode_rate(lq_hwrate, info->band,
&lq_rate)) {
WARN_ON_ONCE(1);
return;
}
/* Only collect stats if retried rate is in the same RS
* table as active/search.
*/
if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
tmp_tbl = curr_tbl;
else if (rs_rate_column_match(&lq_rate,
&other_tbl->rate))
tmp_tbl = other_tbl;
else
continue;
rs_collect_tpc_data(mvm, lq_sta, tmp_tbl,
tx_resp_rate.index, 1,
i < retries ? 0 : legacy_success,
reduced_txp);
rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl,
tx_resp_rate.index, 1,
i < retries ? 0 : legacy_success);
}
/* Update success/fail counts if not searching for new mode */
if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
lq_sta->total_success += legacy_success;
lq_sta->total_failed += retries + (1 - legacy_success);
}
}
/* The last TX rate is cached in lq_sta; it's set in if/else above */
lq_sta->last_rate_n_flags = lq_hwrate;
IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
done:
/* See if there's a better rate or modulation mode to try. */
if (sta->supp_rates[info->band])
rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp);
}
void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int tid, struct ieee80211_tx_info *info, bool ndp)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
/* If it's locked we are in middle of init flow
* just wait for next tx status to update the lq_sta data
*/
if (!spin_trylock(&mvmsta->lq_sta.rs_drv.pers.lock))
return;
__iwl_mvm_rs_tx_status(mvm, sta, tid, info, ndp);
spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock);
}
#ifdef CONFIG_MAC80211_DEBUGFS
static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
struct iwl_lq_cmd *lq_cmd,
@ -3569,7 +3603,7 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED;
bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params);
iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false);
iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd);
ss_params |= LQ_SS_BFER_ALLOWED;
IWL_DEBUG_RATE(mvm,
@ -3735,7 +3769,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
if (lq_sta->pers.dbg_fixed_rate) {
rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL);
iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq);
}
}
@ -4132,10 +4166,15 @@ static const struct rate_control_ops rs_mvm_ops_drv = {
void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band, bool update)
{
if (iwl_mvm_has_tlc_offload(mvm))
if (iwl_mvm_has_tlc_offload(mvm)) {
rs_fw_rate_init(mvm, sta, band, update);
else
rs_drv_rate_init(mvm, sta, band, update);
} else {
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
spin_lock(&mvmsta->lq_sta.rs_drv.pers.lock);
rs_drv_rate_init(mvm, sta, band);
spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock);
}
}
int iwl_mvm_rate_control_register(void)
@ -4165,7 +4204,7 @@ static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
lq->flags &= ~LQ_FLAG_USE_RTS_MSK;
}
return iwl_mvm_send_lq_cmd(mvm, lq, false);
return iwl_mvm_send_lq_cmd(mvm, lq);
}
/**

View File

@ -4,7 +4,7 @@
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
* Copyright(c) 2018 Intel Corporation
* Copyright(c) 2018 - 2019 Intel Corporation
*
* Contact Information:
* Intel Linux Wireless <linuxwifi@intel.com>
@ -390,6 +390,7 @@ struct iwl_lq_sta {
s8 last_rssi;
struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];
struct iwl_mvm *drv;
spinlock_t lock; /* for races in reinit/update table */
} pers;
};

View File

@ -463,20 +463,22 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue,
}
int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
const u8 *data, u32 count)
const u8 *data, u32 count, bool async)
{
struct iwl_rxq_sync_cmd *cmd;
u8 buf[sizeof(struct iwl_rxq_sync_cmd) +
sizeof(struct iwl_mvm_rss_sync_notif)];
struct iwl_rxq_sync_cmd *cmd = (void *)buf;
u32 data_size = sizeof(*cmd) + count;
int ret;
/* should be DWORD aligned */
if (WARN_ON(count & 3 || count > IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE))
/*
* size must be a multiple of DWORD
* Ensure we don't overflow buf
*/
if (WARN_ON(count & 3 ||
count > sizeof(struct iwl_mvm_rss_sync_notif)))
return -EINVAL;
cmd = kzalloc(data_size, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->rxq_mask = cpu_to_le32(rxq_mask);
cmd->count = cpu_to_le32(count);
cmd->flags = 0;
@ -485,9 +487,8 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
ret = iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(DATA_PATH_GROUP,
TRIGGER_RX_QUEUES_NOTIF_CMD),
0, data_size, cmd);
async ? CMD_ASYNC : 0, data_size, cmd);
kfree(cmd);
return ret;
}
@ -503,14 +504,31 @@ static bool iwl_mvm_is_sn_less(u16 sn1, u16 sn2, u16 buffer_size)
!ieee80211_sn_less(sn1, sn2 - buffer_size);
}
static void iwl_mvm_sync_nssn(struct iwl_mvm *mvm, u8 baid, u16 nssn)
{
struct iwl_mvm_rss_sync_notif notif = {
.metadata.type = IWL_MVM_RXQ_NSSN_SYNC,
.metadata.sync = 0,
.nssn_sync.baid = baid,
.nssn_sync.nssn = nssn,
};
iwl_mvm_sync_rx_queues_internal(mvm, (void *)&notif, sizeof(notif));
}
#define RX_REORDER_BUF_TIMEOUT_MQ (HZ / 10)
enum iwl_mvm_release_flags {
IWL_MVM_RELEASE_SEND_RSS_SYNC = BIT(0),
IWL_MVM_RELEASE_FROM_RSS_SYNC = BIT(1),
};
static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
struct napi_struct *napi,
struct iwl_mvm_baid_data *baid_data,
struct iwl_mvm_reorder_buffer *reorder_buf,
u16 nssn)
u16 nssn, u32 flags)
{
struct iwl_mvm_reorder_buf_entry *entries =
&baid_data->entries[reorder_buf->queue *
@ -519,6 +537,18 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
lockdep_assert_held(&reorder_buf->lock);
/*
* We keep the NSSN not too far behind, if we are sync'ing it and it
* is more than 2048 ahead of us, it must be behind us. Discard it.
* This can happen if the queue that hit the 0 / 2048 seqno was lagging
* behind and this queue already processed packets. The next if
* would have caught cases where this queue would have processed less
* than 64 packets, but it may have processed more than 64 packets.
*/
if ((flags & IWL_MVM_RELEASE_FROM_RSS_SYNC) &&
ieee80211_sn_less(nssn, ssn))
goto set_timer;
/* ignore nssn smaller than head sn - this can happen due to timeout */
if (iwl_mvm_is_sn_less(nssn, ssn, reorder_buf->buf_size))
goto set_timer;
@ -529,6 +559,9 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
struct sk_buff *skb;
ssn = ieee80211_sn_inc(ssn);
if ((flags & IWL_MVM_RELEASE_SEND_RSS_SYNC) &&
(ssn == 2048 || ssn == 0))
iwl_mvm_sync_nssn(mvm, baid_data->baid, ssn);
/*
* Empty the list. Will have more than one frame for A-MSDU.
@ -615,7 +648,8 @@ void iwl_mvm_reorder_timer_expired(struct timer_list *t)
sta_id, sn);
iwl_mvm_event_frame_timeout_callback(buf->mvm, mvmsta->vif,
sta, baid_data->tid);
iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, buf, sn);
iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data,
buf, sn, IWL_MVM_RELEASE_SEND_RSS_SYNC);
rcu_read_unlock();
} else {
/*
@ -657,7 +691,8 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue,
spin_lock_bh(&reorder_buf->lock);
iwl_mvm_release_frames(mvm, sta, NULL, ba_data, reorder_buf,
ieee80211_sn_add(reorder_buf->head_sn,
reorder_buf->buf_size));
reorder_buf->buf_size),
0);
spin_unlock_bh(&reorder_buf->lock);
del_timer_sync(&reorder_buf->reorder_timer);
@ -665,8 +700,54 @@ out:
rcu_read_unlock();
}
void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
int queue)
static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm,
struct napi_struct *napi,
u8 baid, u16 nssn, int queue,
u32 flags)
{
struct ieee80211_sta *sta;
struct iwl_mvm_reorder_buffer *reorder_buf;
struct iwl_mvm_baid_data *ba_data;
IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n",
baid, nssn);
if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID ||
baid >= ARRAY_SIZE(mvm->baid_map)))
return;
rcu_read_lock();
ba_data = rcu_dereference(mvm->baid_map[baid]);
if (WARN_ON_ONCE(!ba_data))
goto out;
sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]);
if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta)))
goto out;
reorder_buf = &ba_data->reorder_buf[queue];
spin_lock_bh(&reorder_buf->lock);
iwl_mvm_release_frames(mvm, sta, napi, ba_data,
reorder_buf, nssn, flags);
spin_unlock_bh(&reorder_buf->lock);
out:
rcu_read_unlock();
}
static void iwl_mvm_nssn_sync(struct iwl_mvm *mvm,
struct napi_struct *napi, int queue,
const struct iwl_mvm_nssn_sync_data *data)
{
iwl_mvm_release_frames_from_notif(mvm, napi, data->baid,
data->nssn, queue,
IWL_MVM_RELEASE_FROM_RSS_SYNC);
}
void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, int queue)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rxq_sync_notification *notif;
@ -687,6 +768,10 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
case IWL_MVM_RXQ_NOTIF_DEL_BA:
iwl_mvm_del_ba(mvm, queue, (void *)internal_notif->data);
break;
case IWL_MVM_RXQ_NSSN_SYNC:
iwl_mvm_nssn_sync(mvm, napi, queue,
(void *)internal_notif->data);
break;
default:
WARN_ONCE(1, "Invalid identifier %d", internal_notif->type);
}
@ -785,7 +870,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
}
if (ieee80211_is_back_req(hdr->frame_control)) {
iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn);
iwl_mvm_release_frames(mvm, sta, napi, baid_data,
buffer, nssn, 0);
goto drop;
}
@ -794,7 +880,10 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
* If the SN is smaller than the NSSN it might need to first go into
* the reorder buffer, in which case we just release up to it and the
* rest of the function will take care of storing it and releasing up to
* the nssn
* the nssn.
* This should not happen. This queue has been lagging and it should
* have been updated by a IWL_MVM_RXQ_NSSN_SYNC notification. Be nice
* and update the other queues.
*/
if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size,
buffer->buf_size) ||
@ -802,7 +891,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn;
iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer,
min_sn);
min_sn, IWL_MVM_RELEASE_SEND_RSS_SYNC);
}
/* drop any oudated packets */
@ -813,8 +902,23 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) {
if (iwl_mvm_is_sn_less(buffer->head_sn, nssn,
buffer->buf_size) &&
(!amsdu || last_subframe))
(!amsdu || last_subframe)) {
/*
* If we crossed the 2048 or 0 SN, notify all the
* queues. This is done in order to avoid having a
* head_sn that lags behind for too long. When that
* happens, we can get to a situation where the head_sn
* is within the interval [nssn - buf_size : nssn]
* which will make us think that the nssn is a packet
* that we already freed because of the reordering
* buffer and we will ignore it. So maintain the
* head_sn somewhat updated across all the queues:
* when it crosses 0 and 2048.
*/
if (sn == 2048 || sn == 0)
iwl_mvm_sync_nssn(mvm, baid, sn);
buffer->head_sn = nssn;
}
/* No need to update AMSDU last SN - we are moving the head */
spin_unlock_bh(&buffer->lock);
return false;
@ -829,8 +933,11 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
* while technically there is no hole and we can move forward.
*/
if (!buffer->num_stored && sn == buffer->head_sn) {
if (!amsdu || last_subframe)
if (!amsdu || last_subframe) {
if (sn == 2048 || sn == 0)
iwl_mvm_sync_nssn(mvm, baid, sn);
buffer->head_sn = ieee80211_sn_inc(buffer->head_sn);
}
/* No need to update AMSDU last SN - we are moving the head */
spin_unlock_bh(&buffer->lock);
return false;
@ -875,7 +982,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
* release notification with up to date NSSN.
*/
if (!amsdu || last_subframe)
iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn);
iwl_mvm_release_frames(mvm, sta, napi, baid_data,
buffer, nssn,
IWL_MVM_RELEASE_SEND_RSS_SYNC);
spin_unlock_bh(&buffer->lock);
return true;
@ -1840,40 +1949,14 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
out:
rcu_read_unlock();
}
void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, int queue)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_frame_release *release = (void *)pkt->data;
struct ieee80211_sta *sta;
struct iwl_mvm_reorder_buffer *reorder_buf;
struct iwl_mvm_baid_data *ba_data;
int baid = release->baid;
IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n",
release->baid, le16_to_cpu(release->nssn));
if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID))
return;
rcu_read_lock();
ba_data = rcu_dereference(mvm->baid_map[baid]);
if (WARN_ON_ONCE(!ba_data))
goto out;
sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]);
if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta)))
goto out;
reorder_buf = &ba_data->reorder_buf[queue];
spin_lock_bh(&reorder_buf->lock);
iwl_mvm_release_frames(mvm, sta, napi, ba_data, reorder_buf,
le16_to_cpu(release->nssn));
spin_unlock_bh(&reorder_buf->lock);
out:
rcu_read_unlock();
iwl_mvm_release_frames_from_notif(mvm, napi, release->baid,
le16_to_cpu(release->nssn),
queue, 0);
}

View File

@ -1684,6 +1684,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
*/
if (iwl_mvm_has_tlc_offload(mvm))
iwl_mvm_rs_add_sta(mvm, mvm_sta);
else
spin_lock_init(&mvm_sta->lq_sta.rs_drv.pers.lock);
iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant);
@ -2421,7 +2423,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
static void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm, u8 baid)
{
struct iwl_mvm_delba_notif notif = {
struct iwl_mvm_rss_sync_notif notif = {
.metadata.type = IWL_MVM_RXQ_NOTIF_DEL_BA,
.metadata.sync = 1,
.delba.baid = baid,
@ -2972,7 +2974,7 @@ out:
IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
sta->addr, tid);
return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq, false);
return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq);
}
static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,

View File

@ -343,9 +343,17 @@ struct iwl_mvm_delba_data {
u32 baid;
} __packed;
struct iwl_mvm_delba_notif {
struct iwl_mvm_nssn_sync_data {
u32 baid;
u32 nssn;
} __packed;
struct iwl_mvm_rss_sync_notif {
struct iwl_mvm_internal_rxq_notif metadata;
struct iwl_mvm_delba_data delba;
union {
struct iwl_mvm_delba_data delba;
struct iwl_mvm_nssn_sync_data nssn_sync;
};
} __packed;
/**

View File

@ -831,6 +831,7 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
unsigned int tcp_payload_len;
unsigned int mss = skb_shinfo(skb)->gso_size;
bool ipv4 = (skb->protocol == htons(ETH_P_IP));
bool qos = ieee80211_is_data_qos(hdr->frame_control);
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
skb_shinfo(skb)->gso_size = num_subframes * mss;
@ -864,7 +865,7 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
if (tcp_payload_len > mss) {
skb_shinfo(tmp)->gso_size = mss;
} else {
if (ieee80211_is_data_qos(hdr->frame_control)) {
if (qos) {
u8 *qc;
if (ipv4)

View File

@ -653,12 +653,12 @@ int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id,
* this case to clear the state indicating that station creation is in
* progress.
*/
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool sync)
int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq)
{
struct iwl_host_cmd cmd = {
.id = LQ_CMD,
.len = { sizeof(struct iwl_lq_cmd), },
.flags = sync ? 0 : CMD_ASYNC,
.flags = CMD_ASYNC,
.data = { lq, },
};

View File

@ -604,10 +604,13 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x4234, iwl9560_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2526, 0x42A4, iwl9462_2ac_cfg_soc)},
{IWL_PCI_DEVICE(0x2526, 0x6010, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x6014, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x8014, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x2526, 0x8010, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x2526, 0xE010, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x2526, 0xE014, iwl9260_2ac_160_cfg)},
{IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)},
{IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)},
{IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)},

View File

@ -435,6 +435,8 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans,
DMA_TO_DEVICE);
}
meta->tbs = 0;
if (trans->cfg->use_tfh) {
struct iwl_tfh_tfd *tfd_fh = (void *)tfd;

View File

@ -124,6 +124,7 @@ enum {
#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S)
#define WPA_GTK_OUI_OFFSET 2
#define RSN_GTK_OUI_OFFSET 2
#define MWIFIEX_OUI_NOT_PRESENT 0

View File

@ -181,7 +181,8 @@ mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
u8 ret = MWIFIEX_OUI_NOT_PRESENT;
if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) {
iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data;
iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
WPA_GTK_OUI_OFFSET);
oui = &mwifiex_wpa_oui[cipher][0];
ret = mwifiex_search_oui_in_ie(iebody, oui);
if (ret)