Why Wi-Fi slows after CSI is enabled: a measured vow_atf and 40 MHz client investigation
By Wu Jun · Updated 2026-11-02 · 5 min read
This was one of the hardest problems we hit after putting CSI into a real home network. It combined client idle state, a firmware tuning switch and bandwidth negotiation, so a single speed test was easy to misread:
On the same router, once CSI was enabled, one Mac's downlink dropped from 300–400 Mbps to 6–10 Mbps, while its uplink stayed at ~45 Mbps.
Below is the full isolation process, the reproducible trigger conditions and the two fixes. Every number is a first-party measurement (grade E). The public 10-minute CSI dataset [1] does not contain these throughput logs, so this article publishes the experiment order, conditions, result tables and toolkit [2]; the raw speed-test logs stay private because they contain home-network details. The driver involved is the upstream open-source mt76 [3].
1. Narrow it to "wireless downlink"
Four control experiments ruled out everything else:
| Hypothesis | Experiment | Result |
|---|---|---|
| WAN / ISP problem | download a domestic mirror from the router itself | 264–300 Mbps (391 Mbps with 4 threads) — normal |
| Wired problem | check WAN link negotiation | 1000 Mbps full duplex, zero error counters |
| CSI capture overhead | disable CSI (CSI enable: 0) and retest |
still collapses → not caused by CSI capture |
| Client TX capability | Mac uplink over SSH | 188 Mbps, AP-side rx rate 600 Mbps 3SS — normal |
Conclusion: only the AP→client (downlink) direction collapses, pinned to VHT-MCS0 1SS (13.5 Mbps PHY).
2. The key clue: it collapses when idle, and does not recover
Watching the client's live rate from the router side:
iw dev <iface> station dump | grep -E "tx bitrate|rx bitrate|signal"
What we saw:
- about 1 second after going idle, the AP's transmit rate for that client drops to MCS0;
- under continuous traffic it sometimes climbs back to MCS9 (~400 Mbps) — and sometimes never recovers;
- ping between client and AP: 0% loss, but RTT jitter up to 243 ms (a power-save / buffering signature).
In the "stuck" state, even a continuous 36-second saturated download stayed at 7–10 Mbps.
3. High-confidence trigger #1: VoW ATF prevents rate recovery
mt76 exposes a firmware feature switch in debugfs:
cat /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf # default = 1
A/B test (same client, same time window, only this switch changed; each run is "60 s idle, then 6 consecutive downloads"):
vow_atf |
Results over 6 runs (Mbps) | Behaviour |
|---|---|---|
| 1 (firmware default) | 9.5 / 7.3 / 9.5 / 7.4 / 9.6 / 7.2 | 36 s of continuous download never recovers |
| 0 (disabled) | 7.8 / 8.8 / 20 / 146 / 387 / 393 | climbs back to full speed within ~15–20 s |
| 0 + client re-association | 369 / 374 / 376 / 390 / 409 / 397 | full speed immediately, 6/6 |
Reproducible repair switch (made persistent): disable vow_atf and replay the setting at boot and every minute
in the watchdog, so it
survives driver reloads and reboots:
echo 0 > /sys/kernel/debug/ieee80211/phy*/mt76/vow_atf
The corresponding scripts are published in the stability toolkit, with a SHA-256 checksum.
4. Trigger condition #2: a 40 MHz client inside an 80 MHz BSS
With ATF fixed, a periodic collapse remained. Further A/B testing showed:
| Client | Width | Stability |
|---|---|---|
| older Mac (Broadcom, VHT 3SS) | 40 MHz | collapses repeatedly |
| phone (HE 2SS) | 80 MHz | stable at 1200 Mbps |
| other devices (VHT 1SS) | 80 MHz | stable |
Switching the 5 GHz radio from 80 MHz to 40 MHz (htmode=HE40):
| Setting | 60 s idle, then 6 consecutive downloads |
|---|---|
| HE80 (80 MHz) | 7.4 / 7.3 / 9.3 / 9.5 / 9.6 / 9.6 Mbps (all collapsed) |
| HE40 (40 MHz) | 384 / 375 / 328 / 416 / 411 / 419 Mbps (6/6 stable) |
Conclusion: a client that only negotiates 40 MHz is incompatible with the 80 MHz BSS in this MT7986 + mt76 firmware combination. Native-width operation makes the problem disappear. Because we did not instrument the firmware's internal rate state, we stop at the reproducible condition and repair rather than claiming a specific PPDU field or register-level root cause.
These are grade E field results. The method and repair scripts are in the public toolkit [2]; the raw throughput logs are not part of the public CSI dataset [1].
5. Choosing between the two fixes
| Goal | Setting | Cost |
|---|---|---|
| keep high-resolution CSI (80 MHz / 256 subcarriers) | stay on HE80 |
the 40 MHz client is slow |
| keep daily usability (client at full speed) | switch to HE40 |
CSI bandwidth and subcarrier count halve |
A ~30-second switch script lets you move between them on demand:
sh /usr/bin/wifi-htmode HE80 # CSI experiment mode
sh /usr/bin/wifi-htmode HE40 # daily mode
6. A decision flow you can reuse
Wi-Fi got slow
├─ only downlink slow, uplink fine? ──▶ check the AP-side tx bitrate for MCS0
│ ├─ collapses when idle, never recovers ──▶ disable vow_atf (and persist it)
│ └─ still collapses periodically ───────▶ compare client width vs BSS width
├─ both directions slow ──▶ look at interference / channel / negotiation (not covered here)
└─ wired is slow too ─────▶ check WAN negotiation and ISP (WAN stayed 1000 Mbps in our case)
If your router is also MT7986/MT7915 class (OpenWrt + mt76), run the two tables above first — five minutes tells you which of the two cases you are in.
7. Thirty-minute minimum reproduction
- Record router model, OpenWrt/mt76 version, Wi-Fi firmware MD5, channel, width, country and TX power;
- record client model, negotiated width, spatial streams, RSSI/SNR and power-save settings;
- test uplink, downlink and wired paths to confirm the problem is AP-to-client only;
- sample
tx bitrate/rx bitraterepeatedly rather than trusting one reading; - idle for 60 seconds, then run six 6-second LAN downloads, recording PHY rate, achieved throughput and bytes on both ends;
- change only
vow_atfand repeat step 5; - restore
vow_atf, change the BSS from HE80 to HE40 and repeat step 5; - restore the original configuration and keep timestamps, command output and client state for every step.
Suggested minimum result table:
| Experiment | Client / BSS | Six achieved downlinks | Conclusion |
|---|---|---|---|
| baseline | 40 / 80 MHz | 7-10 Mbps | reproduced |
| ATF off only | 40 / 80 MHz | 8 to 393 Mbps | ATF correlates with recovery |
| native width | 40 / 40 MHz | 328-419 Mbps | wide-BSS condition removed |
8. Evidence boundary
- the six-run
vow_atf=1/0comparison supports a strong association with rate recovery on this device; it does not prove the firmware's internal causal mechanism; - the HE80/HE40 comparison supports an association between the BSS/client width mismatch and the observed failure; repeat the test on other hardware;
- this investigation did not decompile firmware or capture internal rate-control state, so it does not claim a specific register-level root cause;
- the repair changes observable conditions; it is not a binary patch to MediaTek firmware;
- other firmware versions, client chips and power-save policies may not reproduce, so run the A/B test on your hardware.
9. Reporting template
router: GL-MT6000 / OpenWrt ... / mt76 ...
firmware: mt7986_wm.bin MD5 ...
client: model / chip / VHT or HE / NSS / negotiated width
radio: band / channel / width / country / txpower
problem: downlink only? idle trigger? persistent?
baseline: 6x6s results, tx/rx bitrate, bytes AP TX vs client RX
vow_atf=0: 6x6s results
HE40: 6x6s results
restored state: yes/no
Without these fields, maintainers cannot separate driver, firmware, client power-save and environmental effects.
Related: 80 MHz vs 40 MHz · mt76 rate control deep dive · OpenWrt Wi-Fi slow after idle · router CSI guide
References and data sources
Every external claim carries a source and an evidence grade (grading scheme). Grade E marks first-party, reproducible measurements from this project.
- Grade E · first-party measurement
This project's 10-minute raw capture (57,293 records / 600.0 s, SHA-256 00373f83…95e2)
Router CSI Lab · 2026
okwujun.pw/en/datasets/ · verified: Reassembled chunks verified byte-identical (2026-10-11) · The local raw master has SHA-256 00373f83…95e2; the public de-identified derivative has SHA-256 527466ce…a440. They must not be confused. - Grade E · first-party measurement
Router CSI Toolkit — capture/analysis/watchdog scripts (MIT, public repository)
Router CSI Lab · 2026
github.com/jjjwu/router-csi-toolkit · verified: GitHub API verified the public repository, capture-quality preflight, successful CI and the v0.1.3 release on 2026-11-02; the archive SHA-256 is 59f144c0…adea7 · The public repository includes a dependency-free capture preflight; the browser checker uses the same container definition and evidence boundaries. - Grade C · official docs
openwrt/mt76 — mac80211 driver for MediaTek Wi-Fi chipsets
OpenWrt project / MediaTek
github.com/openwrt/mt76 · verified: GitHub API 2026-10-11 (stars 890, last push 2026-10-08) · Upstream of the open-source mt76 driver used here; the CSI patch is applied on top of this codebase.