From 40eb36e01ab26956dd2a35fd1d3f7fc3251ddda3 Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 8 Jul 2026 14:34:43 +0300 Subject: [PATCH] =?UTF-8?q?iwlwifi:=20Minstrel=20rate=20control=20?= =?UTF-8?q?=E2=80=94=20statistics=20accumulator=20+=20rate=20selection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../redbear-iwlwifi/source/src/linux_mvm.c | 102 ++++++++++++++++++ .../redbear-iwlwifi/source/src/linux_mvm.h | 35 ++++++ .../redbear-iwlwifi/source/src/linux_port.c | 7 +- 3 files changed, 141 insertions(+), 3 deletions(-) diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c index 365a551327..3f4cf33ef9 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c @@ -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; +} diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.h b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.h index a9118638ca..21f17024fa 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.h +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.h @@ -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 */ diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c index 10b81174af..6cb4a4d718 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c @@ -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); } } }