mt76 速率控制:为什么客户端会被钉在 MCS0(以及如何证明)

作者 Wu Jun · 更新于 2026-10-29 · 约 5 分钟

如果一台 OpenWrt + mt76 的 AP 只对某一个客户端发 MCS0,而其他客户端一切正常,那不是错觉—— 而且通常不是信号问题。下面是这次的实测调查。

1. 先看对计数器

iw dev <ap> station dump | grep -E "Station|tx bitrate|rx bitrate|tx failed|tx retries|expected"
字段 含义 陷阱
tx bitrate AP→客户端最后一次用的速率 它是采样值、不是均值——要反复采样
rx bitrate 客户端→AP 的最后速率 下行塌陷时它通常仍然正常
tx failed / tx retries 链路层结果 要和"移动的字节数"对比,不能只看帧数

还要用一次廉价的内网传输确认方向,并在两端统计字节数(见第 3 节)。 如果 AP 发出的字节远多于客户端收到的,那是重传风暴;如果两边差不多,那只是空口空闲。

2. mt76 的速率控制在哪里

cat /sys/kernel/debug/ieee80211/phy*/rate_ctrl_alg      # -> hw/driver
cat /sys/kernel/debug/ieee80211/phy*/hwflags            # -> HAS_RATE_CONTROL

hw/driver 加上 HAS_RATE_CONTROL 意味着速率由固件/SoC 决定——mac80211 的软件速率控制(minstrel) 并不负责。所以"换个速率算法"不是选项;你只能间接影响它。

实践中有两个 debugfs 开关值得关注:

for f in /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf; do
  printf '%s: ' "$f"; cat "$f"                    # 固件空口公平性
done
for f in /sys/kernel/debug/ieee80211/phy*/mt76/aql_enable; do
  printf '%s: ' "$f"; cat "$f"                    # 空口队列上限
done

不要直接重定向到带通配符的路径。路由器可能暴露多个 PHY,此时 shell 会因为目标不唯一而拒绝重定向。 应当遍历匹配到的文件,并且只修改证据清单里记录过的那一个射频。

3. 定位到缺陷的那次测量

同一客户端、同一时段,只改 vow_atf(E 级证据——原始抓包与产生它们的脚本已公开 [1] [2])。空闲 60 秒,然后连续 6 次 6 秒的内网下载:

vow_atf 结果 行为
1(默认) 9.5 / 7.3 / 9.5 / 7.4 / 9.6 / 7.2 Mbps 连续 36 秒满载也完全不恢复
0 7.8 / 8.8 / 20 / 146 / 387 / 393 Mbps 约 15 秒内爬回满速
0 + 客户端重新关联 369 / 374 / 376 / 390 / 409 / 397 Mbps 第一次突发就满速

塌陷状态下的字节对账:12 秒传输,AP 发出 12.87MB,客户端收到 12.4MB。 几乎没有重传浪费——速率很低、信道大部分空闲。这就排除了"客户端收不到",指向速率选择。

4. 当前证据支持的触发条件

两个互相独立的触发条件,在 MT7986 + mt76 上都能复现:

  1. 客户端空闲/省电行为:约 1 秒空闲就足以让 AP 的发送速率掉到 MCS0 1SS。 在 vow_atf=1 下,即使随后持续满载,它也一直待在那里。
  2. 窄带客户端在宽带 BSS 里:只支持 40MHz 的客户端放进 80MHz BSS 会周期性塌陷,与 ATF 开关独立。 用它的原生带宽(40MHz BSS)运行则稳定在 380–420Mbps。这里只确认可复现条件,不宣称已经定位到某个 固件寄存器。

5. 该怎么做

# 1) 关掉那个阻止速率恢复的固件特性(并持久化)
for f in /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf; do
  echo 0 >"$f"
done

# 2) 如果客户端只支持 40MHz,就让射频宽度与它一致
sh /usr/bin/wifi-htmode HE40      # CSI 分辨率下降;客户端恢复可用

# 3) 最后一招:强制一次全新关联(重置速率状态)
iw dev <ap> station del <client-mac>

第 1 步持久化很重要:debugfs 的值会在驱动重载后丢失。本项目工具箱提供了开机初始化脚本与 每分钟重放的 watchdog(scripts/mt76-wifi-tune、scripts/csi-watchdog.sh)。

可直接下载:Router CSI Toolkit v0.1.3 与 SHA-256。

6. 可复现的 20 分钟 A/B 测试

只改一个 debugfs 数值不算证据。测试前先记录射频、客户端、距离、信道、带宽、固件,以及你愿意接受的 最低恢复标准。本文把基线定义为客户端已关联并预热后的 6 次传输中位数;通过条件可设为“20 秒内恢复到 基线的至少 70%”。这个阈值只是示例,但必须在看到结果前确定,不能事后挑一个有利的数字。

先保存一组只读证据:

AP=csi0
CLIENT_MAC='<client-mac>'
OUT="/tmp/mt76-ab-$(date +%s)"
mkdir -p "$OUT"

{
  date +%Y-%m-%dT%H:%M:%S%z
  uname -a
  ubus call system board
  iw dev "$AP" info
  iw phy "$(cat /sys/class/ieee80211/phy*/name 2>/dev/null | head -n1)" info 2>/dev/null || true
  cat /sys/kernel/debug/ieee80211/phy*/rate_ctrl_alg
  cat /sys/kernel/debug/ieee80211/phy*/hwflags
  for f in /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf; do
    printf '%s: ' "$f"; cat "$f"
  done
  dmesg | tail -n 120
} >"$OUT/context.txt" 2>&1

测试负载必须走内网,不要使用公网测速站。客户端静默 60 秒后,连续做 6 次、每次 6 秒的传输, 两次之间停 2 秒;传输期间每秒采样一次站点信息:

for i in $(seq 1 36); do
  printf '\n--- sample %s ---\n' "$i" >>"$OUT/station.log"
  date +%Y-%m-%dT%H:%M:%S%z >>"$OUT/station.log"
  iw dev "$AP" station dump |
    sed -n "/Station $CLIENT_MAC/,/^$/p" >>"$OUT/station.log"
  sleep 1
done

负载可用 iperf3、内网 HTTP 大文件或其他工具,但两组必须完全一致。客户端记录每次吞吐,AP 记录同一时段 收发的字节数。只改变 vow_atf,重复完整流程:

# A 组:写回 context.txt 中记录的原始值
ORIGINAL_VOW_ATF='<original-value>'
for f in /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf; do
  echo "$ORIGINAL_VOW_ATF" >"$f"
done

# B 组:只改变这一个变量
for f in /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf; do
  echo 0 >"$f"
done

两组之间不要改变省电设置、信道、BSS 带宽、天线方向、客户端位置或后台流量。如果客户端只支持 40MHz, 还要另做 HE80 与 HE40 的带宽 A/B。测试结束后恢复原值,除非你明确要用工具箱里的开机脚本和 watchdog 持久化新设置。

证据 为什么需要 何时判测试无效
每组 6 个吞吐值 判断恢复是否重复出现 只保留最好的一次
带时间的 station dump 看见速率如何变化 只采样一次速率
AP/客户端字节数 区分低物理速率与重传丢包 超过约 5% 的差异无法解释
射频与客户端带宽 排除 40MHz 客户端进 80MHz BSS 的混杂因素 中途换了带宽或信道
固件和驱动标识 让结论能对应到具体构建 测试过程中换了固件

预期结果不一定是“B 组每次都从 0 变 400Mbps”。在本次实测里,vow_atf=0 的前几次传输仍然很慢, 约 15 秒后才逐步收敛。真正要验证的是:持续负载下速率是否会收敛,以及这种收敛能否重复。 如果两组都一直快、都一直慢,或者改回一个变量后结果完全不随之变化,只能判为“不确定”, 不能宣称已经找到根因。

7. 不该下的结论

相关:80MHz vs 40MHz · OpenWrt 空闲后变慢 · 路由器 CSI 复现

参考与数据来源

本文的每条外部结论都标注来源与证据等级(分级标准);标注 E 级的是本项目可复现的实测数据。

  1. E 级 · 本项目实测 本项目 10 分钟实测抓包(57,293 条 / 600.0 s,SHA-256 00373f83…95e2)
    Router CSI Lab · 2026
    okwujun.pw/zh/datasets/ · 核实: 分片重组逐字节校验通过(2026-10-11) · 本地原始母片 SHA-256 为 00373f83…95e2;公开下载的是去标识派生文件,SHA-256 为 527466ce…a440,两者不可混用。
  2. E 级 · 本项目实测 Router CSI Toolkit — 采集/分析/守护脚本(MIT,公开仓库)
    Router CSI Lab · 2026
    github.com/jjjwu/router-csi-toolkit · 核实: GitHub API 于 2026-11-02 核实公开仓库、抓包质量预检、成功 CI 与 v0.1.3 Release;归档 SHA-256 为 59f144c0…adea7 · 公开版含无第三方依赖的抓包质量预检;网页端检查器使用同一容器定义和边界口径。