Commit 2fd647f8 authored by Eliad Peller's avatar Eliad Peller Committed by Emmanuel Grumbach

iwlwifi: mvm: add ATPC implementation

Implement Adaptive Tx Power Control algorithm.

ATPC basically tries to decrease the tx power
as much as possible while the throughput is
not being hurt.
Signed-off-by: default avatarEliad Peller <eliadx.peller@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 3a84b69e
...@@ -1215,6 +1215,17 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, ...@@ -1215,6 +1215,17 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT; return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
} }
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
enum ieee80211_band band)
{
u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
if (band != IEEE80211_BAND_2GHZ)
return false;
return bt_activity >= BT_LOW_TRAFFIC;
}
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac) struct ieee80211_tx_info *info, u8 ac)
{ {
......
...@@ -936,6 +936,8 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, ...@@ -936,6 +936,8 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
enum ieee80211_band band);
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac); struct ieee80211_tx_info *info, u8 ac);
int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable); int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable);
......
...@@ -509,6 +509,9 @@ static void rs_rate_scale_clear_tbl_windows(struct iwl_scale_tbl_info *tbl) ...@@ -509,6 +509,9 @@ static void rs_rate_scale_clear_tbl_windows(struct iwl_scale_tbl_info *tbl)
for (i = 0; i < IWL_RATE_COUNT; i++) for (i = 0; i < IWL_RATE_COUNT; i++)
rs_rate_scale_clear_window(&tbl->win[i]); rs_rate_scale_clear_window(&tbl->win[i]);
for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++)
rs_rate_scale_clear_window(&tbl->tpc_win[i]);
} }
static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
...@@ -639,9 +642,11 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, ...@@ -639,9 +642,11 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
} }
static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
int scale_index, int attempts, int successes) int scale_index, int attempts, int successes,
u8 reduced_txp)
{ {
struct iwl_rate_scale_data *window = NULL; struct iwl_rate_scale_data *window = NULL;
int ret;
if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
return -EINVAL; return -EINVAL;
...@@ -649,6 +654,15 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, ...@@ -649,6 +654,15 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
/* Select window for current tx bit rate */ /* Select window for current tx bit rate */
window = &(tbl->win[scale_index]); window = &(tbl->win[scale_index]);
ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes,
window);
if (ret)
return ret;
if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION))
return -EINVAL;
window = &tbl->tpc_win[reduced_txp];
return _rs_collect_tx_data(tbl, scale_index, attempts, successes, return _rs_collect_tx_data(tbl, scale_index, attempts, successes,
window); window);
} }
...@@ -982,6 +996,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, ...@@ -982,6 +996,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
u32 ucode_rate; u32 ucode_rate;
struct rs_rate rate; struct rs_rate rate;
struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
/* Treat uninitialized rate scaling data same as non-existing. */ /* Treat uninitialized rate scaling data same as non-existing. */
if (!lq_sta) { if (!lq_sta) {
...@@ -1106,7 +1121,8 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, ...@@ -1106,7 +1121,8 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
rs_collect_tx_data(curr_tbl, rate.index, rs_collect_tx_data(curr_tbl, rate.index,
info->status.ampdu_len, info->status.ampdu_len,
info->status.ampdu_ack_len); info->status.ampdu_ack_len,
reduced_txp);
/* Update success/fail counts if not searching for new mode */ /* Update success/fail counts if not searching for new mode */
if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
...@@ -1140,7 +1156,8 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, ...@@ -1140,7 +1156,8 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
continue; continue;
rs_collect_tx_data(tmp_tbl, rate.index, 1, rs_collect_tx_data(tmp_tbl, rate.index, 1,
i < retries ? 0 : legacy_success); i < retries ? 0 : legacy_success,
reduced_txp);
} }
/* Update success/fail counts if not searching for new mode */ /* Update success/fail counts if not searching for new mode */
...@@ -1151,6 +1168,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, ...@@ -1151,6 +1168,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
} }
/* The last TX rate is cached in lq_sta; it's set in if/else above */ /* The last TX rate is cached in lq_sta; it's set in if/else above */
lq_sta->last_rate_n_flags = ucode_rate; lq_sta->last_rate_n_flags = ucode_rate;
IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
done: done:
/* See if there's a better rate or modulation mode to try. */ /* See if there's a better rate or modulation mode to try. */
if (sta && sta->supp_rates[sband->band]) if (sta && sta->supp_rates[sband->band])
...@@ -1724,6 +1742,189 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, ...@@ -1724,6 +1742,189 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
return action; return action;
} }
static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
int *weaker, int *stronger)
{
*weaker = index + TPC_TX_POWER_STEP;
if (*weaker > TPC_MAX_REDUCTION)
*weaker = TPC_INVALID;
*stronger = index - TPC_TX_POWER_STEP;
if (*stronger < 0)
*stronger = TPC_INVALID;
}
static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct rs_rate *rate,
enum ieee80211_band band)
{
int index = rate->index;
/*
* allow tpc only if power management is enabled, or bt coex
* activity grade allows it and we are on 2.4Ghz.
*/
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM &&
!iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
return false;
IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type);
if (is_legacy(rate))
return index == IWL_RATE_54M_INDEX;
if (is_ht(rate))
return index == IWL_RATE_MCS_7_INDEX;
if (is_vht(rate))
return index == IWL_RATE_MCS_7_INDEX ||
index == IWL_RATE_MCS_8_INDEX ||
index == IWL_RATE_MCS_9_INDEX;
WARN_ON_ONCE(1);
return false;
}
enum tpc_action {
TPC_ACTION_STAY,
TPC_ACTION_DECREASE,
TPC_ACTION_INCREASE,
TPC_ACTION_NO_RESTIRCTION,
};
static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm,
s32 sr, int weak, int strong,
int current_tpt,
int weak_tpt, int strong_tpt)
{
/* stay until we have valid tpt */
if (current_tpt == IWL_INVALID_VALUE) {
IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n");
return TPC_ACTION_STAY;
}
/* Too many failures, increase txp */
if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) {
IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
return TPC_ACTION_NO_RESTIRCTION;
}
/* try decreasing first if applicable */
if (weak != TPC_INVALID) {
if (weak_tpt == IWL_INVALID_VALUE &&
(strong_tpt == IWL_INVALID_VALUE ||
current_tpt >= strong_tpt)) {
IWL_DEBUG_RATE(mvm,
"no weak txp measurement. decrease txp\n");
return TPC_ACTION_DECREASE;
}
if (weak_tpt > current_tpt) {
IWL_DEBUG_RATE(mvm,
"lower txp has better tpt. decrease txp\n");
return TPC_ACTION_DECREASE;
}
}
/* next, increase if needed */
if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) {
if (weak_tpt == IWL_INVALID_VALUE &&
strong_tpt != IWL_INVALID_VALUE &&
current_tpt < strong_tpt) {
IWL_DEBUG_RATE(mvm,
"higher txp has better tpt. increase txp\n");
return TPC_ACTION_INCREASE;
}
if (weak_tpt < current_tpt &&
(strong_tpt == IWL_INVALID_VALUE ||
strong_tpt > current_tpt)) {
IWL_DEBUG_RATE(mvm,
"lower txp has worse tpt. increase txp\n");
return TPC_ACTION_INCREASE;
}
}
IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n");
return TPC_ACTION_STAY;
}
static bool rs_tpc_perform(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
struct iwl_lq_sta *lq_sta,
struct iwl_scale_tbl_info *tbl)
{
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
struct ieee80211_vif *vif = mvm_sta->vif;
struct ieee80211_chanctx_conf *chanctx_conf;
enum ieee80211_band band;
struct iwl_rate_scale_data *window;
struct rs_rate *rate = &tbl->rate;
enum tpc_action action;
s32 sr;
u8 cur = lq_sta->lq.reduced_tpc;
int current_tpt;
int weak, strong;
int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
if (WARN_ON(!chanctx_conf))
band = IEEE80211_NUM_BANDS;
else
band = chanctx_conf->def.chan->band;
rcu_read_unlock();
if (!rs_tpc_allowed(mvm, rate, band)) {
IWL_DEBUG_RATE(mvm,
"tpc is not allowed. remove txp restrictions");
lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
return cur != TPC_NO_REDUCTION;
}
rs_get_adjacent_txp(mvm, cur, &weak, &strong);
/* Collect measured throughputs for current and adjacent rates */
window = tbl->tpc_win;
sr = window[cur].success_ratio;
current_tpt = window[cur].average_tpt;
if (weak != TPC_INVALID)
weak_tpt = window[weak].average_tpt;
if (strong != TPC_INVALID)
strong_tpt = window[strong].average_tpt;
IWL_DEBUG_RATE(mvm,
"(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n",
cur, current_tpt, sr, weak, strong,
weak_tpt, strong_tpt);
action = rs_get_tpc_action(mvm, sr, weak, strong,
current_tpt, weak_tpt, strong_tpt);
/* override actions if we are on the edge */
if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) {
IWL_DEBUG_RATE(mvm, "already in lowest txp, stay");
action = TPC_ACTION_STAY;
} else if (strong == TPC_INVALID &&
(action == TPC_ACTION_INCREASE ||
action == TPC_ACTION_NO_RESTIRCTION)) {
IWL_DEBUG_RATE(mvm, "already in highest txp, stay");
action = TPC_ACTION_STAY;
}
switch (action) {
case TPC_ACTION_DECREASE:
lq_sta->lq.reduced_tpc = weak;
return true;
case TPC_ACTION_INCREASE:
lq_sta->lq.reduced_tpc = strong;
return true;
case TPC_ACTION_NO_RESTIRCTION:
lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
return true;
case TPC_ACTION_STAY:
/* do nothing */
break;
}
return false;
}
/* /*
* Do rate scaling and search for new modulation mode. * Do rate scaling and search for new modulation mode.
*/ */
...@@ -1973,6 +2174,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, ...@@ -1973,6 +2174,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
break; break;
case RS_ACTION_STAY: case RS_ACTION_STAY:
/* No change */ /* No change */
update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
break;
default: default:
break; break;
} }
...@@ -2584,6 +2787,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, ...@@ -2584,6 +2787,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
rs_build_rates_table_from_fixed(mvm, lq_cmd, rs_build_rates_table_from_fixed(mvm, lq_cmd,
lq_sta->band, lq_sta->band,
lq_sta->dbg_fixed_rate); lq_sta->dbg_fixed_rate);
lq_cmd->reduced_tpc = 0;
ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
RATE_MCS_ANT_POS; RATE_MCS_ANT_POS;
} else } else
...@@ -2787,6 +2991,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, ...@@ -2787,6 +2991,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
lq_sta->lq.agg_disable_start_th, lq_sta->lq.agg_disable_start_th,
lq_sta->lq.agg_frame_cnt_limit); lq_sta->lq.agg_frame_cnt_limit);
desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
desc += sprintf(buff+desc, desc += sprintf(buff+desc,
"Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
lq_sta->lq.initial_rate_index[0], lq_sta->lq.initial_rate_index[0],
......
...@@ -157,6 +157,13 @@ enum { ...@@ -157,6 +157,13 @@ enum {
#define IWL_RATE_INCREASE_TH 6400 /* 50% */ #define IWL_RATE_INCREASE_TH 6400 /* 50% */
#define RS_SR_FORCE_DECREASE 1920 /* 15% */ #define RS_SR_FORCE_DECREASE 1920 /* 15% */
#define TPC_SR_FORCE_INCREASE 9600 /* 75% */
#define TPC_SR_NO_INCREASE 10880 /* 85% */
#define TPC_TX_POWER_STEP 3
#define TPC_MAX_REDUCTION 15
#define TPC_NO_REDUCTION 0
#define TPC_INVALID 0xff
#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */
#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) #define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000)
#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) #define LINK_QUAL_AGG_TIME_LIMIT_MIN (100)
...@@ -279,6 +286,8 @@ struct iwl_scale_tbl_info { ...@@ -279,6 +286,8 @@ struct iwl_scale_tbl_info {
enum rs_column column; enum rs_column column;
const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
/* per txpower-reduction history */
struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1];
}; };
enum { enum {
...@@ -337,6 +346,9 @@ struct iwl_lq_sta { ...@@ -337,6 +346,9 @@ struct iwl_lq_sta {
u32 last_rate_n_flags; u32 last_rate_n_flags;
/* packets destined for this STA are aggregated */ /* packets destined for this STA are aggregated */
u8 is_agg; u8 is_agg;
/* tx power reduce for this sta */
int tpc_reduce;
}; };
/* Initialize station's rate scaling information after adding station */ /* Initialize station's rate scaling information after adding station */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment