1
0
Fork 0

iwlwifi: mvm: detect U-APSD breaking aggregation

Try to detect that the AP is not using aggregation even when there's
enough traffic to make it worthwhile; if this is the case and U-APSD
is enabled then assume the AP is broken (like so many) and doesn't
enable aggregation when U-APSD is used. In this case, disconnect from
the AP and blacklist U-APSD for a potential new connection to it.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
hifive-unleashed-5.1
Johannes Berg 2014-11-11 12:57:03 +01:00 committed by Luca Coelho
parent bde1492d4a
commit b0ffe455bc
6 changed files with 244 additions and 1 deletions

View File

@ -69,6 +69,8 @@
#include <linux/ieee80211.h>
#define IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM 20
#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
@ -115,6 +117,8 @@
#define IWL_MVM_TCM_LOAD_MEDIUM_THRESH 10 /* percentage */
#define IWL_MVM_TCM_LOAD_HIGH_THRESH 50 /* percentage */
#define IWL_MVM_TCM_LOWLAT_ENABLE_THRESH 100 /* packets/10 seconds */
#define IWL_MVM_UAPSD_NONAGG_PERIOD 5000 /* msecs */
#define IWL_MVM_UAPSD_NOAGG_LIST_LEN IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM
#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1
#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2
#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1

View File

@ -1728,6 +1728,27 @@ iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
return ret ?: count;
}
static ssize_t
iwl_dbgfs_uapsd_noagg_bssids_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
u8 buf[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM * ETH_ALEN * 3 + 1];
unsigned int pos = 0;
size_t bufsz = sizeof(buf);
int i;
mutex_lock(&mvm->mutex);
for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++)
pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
mvm->uapsd_noagg_bssids[i].addr);
mutex_unlock(&mvm->mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
/* Device wide debugfs entries */
@ -1762,6 +1783,8 @@ MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl,
(IWL_RSS_INDIRECTION_TABLE_SIZE * 2));
MVM_DEBUGFS_WRITE_FILE_OPS(inject_packet, 512);
MVM_DEBUGFS_READ_FILE_OPS(uapsd_noagg_bssids);
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
@ -1972,6 +1995,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
mvm->debugfs_dir, &mvm->drop_bcn_ap_mode))
goto err;
MVM_DEBUGFS_ADD_FILE(uapsd_noagg_bssids, mvm->debugfs_dir, S_IRUSR);
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
bcast_dir = debugfs_create_dir("bcast_filtering",

View File

@ -952,6 +952,16 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
switch (action) {
case IEEE80211_AMPDU_RX_START:
if (iwl_mvm_vif_from_mac80211(vif)->ap_sta_id ==
iwl_mvm_sta_from_mac80211(sta)->sta_id) {
struct iwl_mvm_vif *mvmvif;
u16 macid = iwl_mvm_vif_from_mac80211(vif)->id;
struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[macid];
mdata->opened_rx_ba_sessions = true;
mvmvif = iwl_mvm_vif_from_mac80211(vif);
cancel_delayed_work(&mvmvif->uapsd_nonagg_detected_wk);
}
if (!iwl_enable_rx_ampdu(mvm->cfg)) {
ret = -EINVAL;
break;
@ -1435,6 +1445,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
mvm->p2p_device_vif = vif;
}
iwl_mvm_tcm_add_vif(mvm, vif);
if (vif->type == NL80211_IFTYPE_MONITOR)
mvm->monitor_on = true;
@ -1486,6 +1498,10 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
iwl_mvm_prepare_mac_removal(mvm, vif);
if (!(vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_ADHOC))
iwl_mvm_tcm_rm_vif(mvm, vif);
mutex_lock(&mvm->mutex);
if (mvm->bf_allowed_vif == mvmvif) {
@ -2535,6 +2551,16 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
const u8 *bssid)
{
int i;
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
struct iwl_mvm_tcm_mac *mdata;
mdata = &mvm->tcm.data[iwl_mvm_vif_from_mac80211(vif)->id];
ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
mdata->opened_rx_ba_sessions = false;
}
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
return;
@ -2549,6 +2575,13 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return;
}
for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++) {
if (ether_addr_equal(mvm->uapsd_noagg_bssids[i].addr, bssid)) {
vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
return;
}
}
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
}

View File

@ -446,6 +446,8 @@ struct iwl_mvm_vif {
/* FW identified misbehaving AP */
u8 uapsd_misbehaving_bssid[ETH_ALEN];
struct delayed_work uapsd_nonagg_detected_wk;
/* Indicates that CSA countdown may be started */
bool csa_countdown;
bool csa_failed;
@ -621,6 +623,7 @@ struct iwl_mvm_tcm_mac {
struct ewma_rate rate;
bool detected;
} uapsd_nonagg_detect;
bool opened_rx_ba_sessions;
};
struct iwl_mvm_tcm {
@ -1028,6 +1031,10 @@ struct iwl_mvm {
unsigned long bt_coex_last_tcm_ts;
struct iwl_mvm_tcm tcm;
u8 uapsd_noagg_bssid_write_idx;
struct mac_address uapsd_noagg_bssids[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM]
__aligned(2);
struct iwl_time_quota_cmd last_quota_cmd;
#ifdef CONFIG_NL80211_TESTMODE
@ -1963,6 +1970,8 @@ void iwl_mvm_tcm_work(struct work_struct *work);
void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm);
void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel);
void iwl_mvm_resume_tcm(struct iwl_mvm *mvm);
void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
u8 iwl_mvm_tcm_load_percentage(u32 airtime, u32 elapsed);
void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);

View File

@ -264,6 +264,12 @@ static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm,
struct iwl_mvm_tcm_mac *mdata;
int mac;
int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */
struct iwl_mvm_vif *mvmvif;
/* expected throughput in 100Kbps, single stream, 20 MHz */
static const u8 thresh_tpt[] = {
9, 18, 30, 42, 60, 78, 90, 96, 120, 135,
};
u16 thr;
if (ieee80211_is_data_qos(hdr->frame_control))
ac = tid_to_mac80211_ac[ieee80211_get_tid(hdr)];
@ -285,6 +291,35 @@ static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm,
if (!(rate_n_flags & (RATE_MCS_HT_MSK | RATE_MCS_VHT_MSK)))
return;
mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
if (mdata->opened_rx_ba_sessions ||
mdata->uapsd_nonagg_detect.detected ||
(!mvmvif->queue_params[IEEE80211_AC_VO].uapsd &&
!mvmvif->queue_params[IEEE80211_AC_VI].uapsd &&
!mvmvif->queue_params[IEEE80211_AC_BE].uapsd &&
!mvmvif->queue_params[IEEE80211_AC_BK].uapsd) ||
mvmsta->sta_id != mvmvif->ap_sta_id)
return;
if (rate_n_flags & RATE_MCS_HT_MSK) {
thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK];
thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >>
RATE_HT_MCS_NSS_POS);
} else {
if (WARN_ON((rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK) >=
ARRAY_SIZE(thresh_tpt)))
return;
thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK];
thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
RATE_VHT_MCS_NSS_POS);
}
thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) >>
RATE_MCS_CHAN_WIDTH_POS);
mdata->uapsd_nonagg_detect.rx_bytes += len;
ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, thr);
}
static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
@ -693,6 +728,7 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
int expected_size;
int i;
u8 *energy;
__le32 *bytes;
__le32 *air_time;
__le32 flags;
@ -768,11 +804,13 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
struct iwl_notif_statistics_v11 *v11 = (void *)&pkt->data;
energy = (void *)&v11->load_stats.avg_energy;
bytes = (void *)&v11->load_stats.byte_count;
air_time = (void *)&v11->load_stats.air_time;
} else {
struct iwl_notif_statistics_cdb *stats = (void *)&pkt->data;
energy = (void *)&stats->load_stats.avg_energy;
bytes = (void *)&stats->load_stats.byte_count;
air_time = (void *)&stats->load_stats.air_time;
}
@ -802,6 +840,15 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) {
struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i];
u32 airtime = le32_to_cpu(air_time[i]);
u32 rx_bytes = le32_to_cpu(bytes[i]);
mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes;
if (airtime) {
/* re-init every time to store rate from FW */
ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
ewma_rate_add(&mdata->uapsd_nonagg_detect.rate,
rx_bytes * 8 / airtime);
}
mdata->rx.airtime += airtime;
}

View File

@ -1503,12 +1503,109 @@ static void iwl_mvm_tcm_results(struct iwl_mvm *mvm)
mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_tcm_uapsd_nonagg_detected_wk(struct work_struct *wk)
{
struct iwl_mvm *mvm;
struct iwl_mvm_vif *mvmvif;
struct ieee80211_vif *vif;
mvmvif = container_of(wk, struct iwl_mvm_vif,
uapsd_nonagg_detected_wk.work);
vif = container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
mvm = mvmvif->mvm;
if (mvm->tcm.data[mvmvif->id].opened_rx_ba_sessions)
return;
/* remember that this AP is broken */
memcpy(mvm->uapsd_noagg_bssids[mvm->uapsd_noagg_bssid_write_idx].addr,
vif->bss_conf.bssid, ETH_ALEN);
mvm->uapsd_noagg_bssid_write_idx++;
if (mvm->uapsd_noagg_bssid_write_idx >= IWL_MVM_UAPSD_NOAGG_LIST_LEN)
mvm->uapsd_noagg_bssid_write_idx = 0;
iwl_mvm_connection_loss(mvm, vif,
"AP isn't using AMPDU with uAPSD enabled");
}
static void iwl_mvm_uapsd_agg_disconnect_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->mvm;
int *mac_id = data;
if (vif->type != NL80211_IFTYPE_STATION)
return;
if (mvmvif->id != *mac_id)
return;
if (!vif->bss_conf.assoc)
return;
if (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd &&
!mvmvif->queue_params[IEEE80211_AC_VI].uapsd &&
!mvmvif->queue_params[IEEE80211_AC_BE].uapsd &&
!mvmvif->queue_params[IEEE80211_AC_BK].uapsd)
return;
if (mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected)
return;
mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected = true;
IWL_INFO(mvm,
"detected AP should do aggregation but isn't, likely due to U-APSD\n");
schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk, 15 * HZ);
}
static void iwl_mvm_check_uapsd_agg_expected_tpt(struct iwl_mvm *mvm,
unsigned int elapsed,
int mac)
{
u64 bytes = mvm->tcm.data[mac].uapsd_nonagg_detect.rx_bytes;
u64 tpt;
unsigned long rate;
rate = ewma_rate_read(&mvm->tcm.data[mac].uapsd_nonagg_detect.rate);
if (!rate || mvm->tcm.data[mac].opened_rx_ba_sessions ||
mvm->tcm.data[mac].uapsd_nonagg_detect.detected)
return;
if (iwl_mvm_has_new_rx_api(mvm)) {
tpt = 8 * bytes; /* kbps */
do_div(tpt, elapsed);
rate *= 1000; /* kbps */
if (tpt < 22 * rate / 100)
return;
} else {
/*
* the rate here is actually the threshold, in 100Kbps units,
* so do the needed conversion from bytes to 100Kbps:
* 100kb = bits / (100 * 1000),
* 100kbps = 100kb / (msecs / 1000) ==
* (bits / (100 * 1000)) / (msecs / 1000) ==
* bits / (100 * msecs)
*/
tpt = (8 * bytes);
do_div(tpt, elapsed * 100);
if (tpt < rate)
return;
}
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_uapsd_agg_disconnect_iter, &mac);
}
static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm,
unsigned long ts,
bool handle_uapsd)
{
unsigned int elapsed = jiffies_to_msecs(ts - mvm->tcm.ts);
unsigned int uapsd_elapsed =
jiffies_to_msecs(ts - mvm->tcm.uapsd_nonagg_ts);
u32 total_airtime = 0;
int ac, mac;
bool low_latency = false;
@ -1551,6 +1648,12 @@ static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm,
}
low_latency |= mvm->tcm.result.low_latency[mac];
if (!mvm->tcm.result.low_latency[mac] && handle_uapsd)
iwl_mvm_check_uapsd_agg_expected_tpt(mvm, uapsd_elapsed,
mac);
/* clear old data */
if (handle_uapsd)
mdata->uapsd_nonagg_detect.rx_bytes = 0;
memset(&mdata->rx.airtime, 0, sizeof(mdata->rx.airtime));
memset(&mdata->tx.airtime, 0, sizeof(mdata->tx.airtime));
}
@ -1592,7 +1695,8 @@ void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm)
{
unsigned long ts = jiffies;
bool handle_uapsd =
false;
time_after(ts, mvm->tcm.uapsd_nonagg_ts +
msecs_to_jiffies(IWL_MVM_UAPSD_NONAGG_PERIOD));
spin_lock(&mvm->tcm.lock);
if (mvm->tcm.paused || !time_after(ts, mvm->tcm.ts + MVM_TCM_PERIOD)) {
@ -1601,6 +1705,12 @@ void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm)
}
spin_unlock(&mvm->tcm.lock);
if (handle_uapsd && iwl_mvm_has_new_rx_api(mvm)) {
mutex_lock(&mvm->mutex);
if (iwl_mvm_request_statistics(mvm, true))
handle_uapsd = false;
mutex_unlock(&mvm->mutex);
}
spin_lock(&mvm->tcm.lock);
/* re-check if somebody else won the recheck race */
@ -1659,6 +1769,21 @@ void iwl_mvm_resume_tcm(struct iwl_mvm *mvm)
spin_unlock_bh(&mvm->tcm.lock);
}
void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
INIT_DELAYED_WORK(&mvmvif->uapsd_nonagg_detected_wk,
iwl_mvm_tcm_uapsd_nonagg_detected_wk);
}
void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
cancel_delayed_work_sync(&mvmvif->uapsd_nonagg_detected_wk);
}
void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, u32 *gp2, u64 *boottime)
{