iwlwifi: Minstrel rate control — statistics accumulator + rate selection
Basic Minstrel rate adaptation, cross-referenced from Linux 7.1 mvm/rs.h + mvm/rs.c. rb_iwl_mvm_rs_state tracks per-MCS (attempts, successes, success_ratio × 12800). Algorithm: probe alternate rates every 10 frames, promote if success ratio exceeds current best, select best known rate with signal-based upper bound. Uses TX status codes from fw/api/tx.h: TX_STATUS_SUCCESS (0x01), TX_STATUS_FAIL_SHORT_LIMIT (0x82), TX_STATUS_FAIL_LONG_LIMIT (0x83). Wired into iwl_pcie_rx_handle() — rate_idx now comes from rs_select() which adapts based on accumulated statistics instead of using a fixed lookup table. When no TX statistics are available (fresh boot / no firmware feedback), rs_select() falls through to rb_iwl_mvm_rate_to_mcs() as a cold-start default.
This commit is contained in:
@@ -205,3 +205,105 @@ int rb_iwl_mvm_parse_firmware(const uint8_t *data, size_t len,
|
||||
out->parsed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Minstrel rate control — Linux 7.1 mvm/rs.c. */
|
||||
/* */
|
||||
/* Algorithm (simplified from iwl_mvm_rs_rate_init + rs_get_rate): */
|
||||
/* 1. Track per-MCS (attempts, successes) per rate via TX status. */
|
||||
/* 2. success_ratio = successes × 12800 / attempts (scaled by 100×128)*/
|
||||
/* 3. Probe alternate rates every N frames (10% default). */
|
||||
/* 4. Promote probed rate if its success ratio exceeds best rate. */
|
||||
/* 5. Best rate selection biases toward higher MCS when signal good. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
void rb_iwl_mvm_rs_init(struct rb_iwl_mvm_rs_state *rs)
|
||||
{
|
||||
int i;
|
||||
if (!rs) return;
|
||||
memset(rs, 0, sizeof(*rs));
|
||||
for (i = 0; i < RB_IWL_MVM_MAX_RATES; ++i) {
|
||||
rs->rates[i].attempts = 1;
|
||||
rs->rates[i].successes = 0;
|
||||
rs->rates[i].success_ratio = 0;
|
||||
rs->rates[i].average_tpt = 0;
|
||||
rs->rates[i].last_probe = 0;
|
||||
}
|
||||
rs->best_rate = 0;
|
||||
rs->probe_rate = RB_IWL_MVM_RATE_INVALID;
|
||||
rs->probe_count = 0;
|
||||
rs->last_update = 0;
|
||||
}
|
||||
|
||||
void rb_iwl_mvm_rs_update(struct rb_iwl_mvm_rs_state *rs,
|
||||
int rate_idx, int status, int retries)
|
||||
{
|
||||
int success;
|
||||
struct rb_iwl_rate_stats *r;
|
||||
|
||||
if (!rs || rate_idx < 0 || rate_idx >= RB_IWL_MVM_MAX_RATES)
|
||||
return;
|
||||
|
||||
success = (status == RB_IWL_TX_STATUS_SUCCESS) ? 1 : 0;
|
||||
r = &rs->rates[rate_idx];
|
||||
r->attempts += 1 + (uint64_t)retries;
|
||||
r->successes += (uint64_t)success;
|
||||
|
||||
if (r->attempts > 0)
|
||||
r->success_ratio = (int32_t)((r->successes * 12800LL) / r->attempts);
|
||||
else
|
||||
r->success_ratio = 0;
|
||||
|
||||
r->average_tpt = r->success_ratio;
|
||||
|
||||
if (rate_idx == rs->probe_rate && success) {
|
||||
int best_ratio = rs->rates[rs->best_rate].success_ratio;
|
||||
if (r->success_ratio > best_ratio)
|
||||
rs->best_rate = rate_idx;
|
||||
rs->probe_rate = RB_IWL_MVM_RATE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
int rb_iwl_mvm_rs_select(struct rb_iwl_mvm_rs_state *rs, int signal)
|
||||
{
|
||||
int base_rate, i, best;
|
||||
int32_t best_ratio;
|
||||
|
||||
if (!rs)
|
||||
return rb_iwl_mvm_rate_to_mcs(signal);
|
||||
|
||||
base_rate = rb_iwl_mvm_rate_to_mcs(signal);
|
||||
|
||||
/* Probe alternate rate every 10 frames */
|
||||
if (++rs->probe_count >= 10) {
|
||||
rs->probe_count = 0;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
int candidate = base_rate + 1 + i;
|
||||
if (candidate >= RB_IWL_MVM_MAX_RATES)
|
||||
candidate -= RB_IWL_MVM_MAX_RATES;
|
||||
if (candidate != rs->best_rate &&
|
||||
rs->rates[candidate].attempts < 10) {
|
||||
rs->probe_rate = candidate;
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
rs->probe_rate = RB_IWL_MVM_RATE_INVALID;
|
||||
}
|
||||
|
||||
if (rs->probe_rate != RB_IWL_MVM_RATE_INVALID)
|
||||
return rs->probe_rate;
|
||||
|
||||
if (rs->best_rate > 0)
|
||||
return rs->best_rate;
|
||||
|
||||
best = 0;
|
||||
best_ratio = rs->rates[0].success_ratio;
|
||||
for (i = 1; i <= base_rate && i < RB_IWL_MVM_MAX_RATES; ++i) {
|
||||
if (rs->rates[i].success_ratio > best_ratio) {
|
||||
best_ratio = rs->rates[i].success_ratio;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
rs->best_rate = best;
|
||||
return best > 0 ? best : base_rate;
|
||||
}
|
||||
|
||||
@@ -203,4 +203,39 @@ struct rb_iwl_fw_metadata {
|
||||
int rb_iwl_mvm_parse_firmware(const uint8_t *data, size_t len,
|
||||
struct rb_iwl_fw_metadata *out);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Minstrel rate control — Linux 7.1 mvm/rs.h + mvm/rs.c. */
|
||||
/* Per-rate statistics (Linux 7.1 struct iwl_rate_scale_data rs.h:236) */
|
||||
/* TX status codes (Linux 7.1 fw/api/tx.h enum iwl_tx_status:351) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
struct rb_iwl_rate_stats {
|
||||
uint64_t attempts;
|
||||
uint64_t successes;
|
||||
int32_t success_ratio;
|
||||
int32_t average_tpt;
|
||||
int32_t last_probe;
|
||||
};
|
||||
|
||||
#define RB_IWL_TX_STATUS_SUCCESS 0x01
|
||||
#define RB_IWL_TX_STATUS_FAIL_SHORT_LIMIT 0x82
|
||||
#define RB_IWL_TX_STATUS_FAIL_LONG_LIMIT 0x83
|
||||
#define RB_IWL_TX_STATUS_FAIL_LIFE_EXPIRE 0x87
|
||||
|
||||
#define RB_IWL_MVM_MAX_RATES 30
|
||||
#define RB_IWL_MVM_RATE_INVALID (-1)
|
||||
|
||||
struct rb_iwl_mvm_rs_state {
|
||||
struct rb_iwl_rate_stats rates[RB_IWL_MVM_MAX_RATES];
|
||||
int best_rate;
|
||||
int probe_rate;
|
||||
int probe_count;
|
||||
int last_update;
|
||||
};
|
||||
|
||||
void rb_iwl_mvm_rs_init(struct rb_iwl_mvm_rs_state *rs);
|
||||
void rb_iwl_mvm_rs_update(struct rb_iwl_mvm_rs_state *rs,
|
||||
int rate_idx, int status, int retries);
|
||||
int rb_iwl_mvm_rs_select(struct rb_iwl_mvm_rs_state *rs, int signal);
|
||||
|
||||
#endif /* RB_IWLWIFI_MVM_H */
|
||||
|
||||
@@ -275,6 +275,7 @@ struct iwl_trans_pcie {
|
||||
struct rb_iwl_fw_blob_info fw_info;
|
||||
struct rb_iwl_fw_blob_info pnvm_info;
|
||||
struct rb_iwl_fw_metadata fw_meta;
|
||||
struct rb_iwl_mvm_rs_state rs_state;
|
||||
struct rb_iwl_key keys[4];
|
||||
char fw_name_storage[RB_IWL_MAX_FW_NAME];
|
||||
char pnvm_name_storage[RB_IWL_MAX_FW_NAME];
|
||||
@@ -1514,15 +1515,15 @@ static void iwl_pcie_rx_handle(struct iwl_trans_pcie *trans)
|
||||
|
||||
if (desc_fmt == RB_IWL_MVM_RX_RAW_FRAME || desc_fmt == RB_IWL_MVM_RX_UNKNOWN) {
|
||||
rx_status.signal = -42;
|
||||
rx_status.rate_idx = rb_iwl_mvm_rate_to_mcs(rx_status.signal);
|
||||
rx_status.rate_idx = rb_iwl_mvm_rs_select(&trans->rs_state, rx_status.signal);
|
||||
} else {
|
||||
rb_iwl_mvm_extract_signal(buf->addr, buf->size, desc_fmt, &mvm_info);
|
||||
if (mvm_info.signal != 0) {
|
||||
rx_status.signal = mvm_info.signal;
|
||||
rx_status.rate_idx = rb_iwl_mvm_rate_to_mcs(mvm_info.signal);
|
||||
rx_status.rate_idx = rb_iwl_mvm_rs_select(&trans->rs_state, mvm_info.signal);
|
||||
} else {
|
||||
rx_status.signal = -42;
|
||||
rx_status.rate_idx = rb_iwl_mvm_rate_to_mcs(-42);
|
||||
rx_status.rate_idx = rb_iwl_mvm_rs_select(&trans->rs_state, -42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user