Recently, I purchased a mini-PC with an embedded Intel Wi-Fi 6 AX200 wireless chipset and installed Gentoo Linux on it. The reason I purchased the mini-PC was to replace my ageing music server (running MPD), but I also wanted to use it for playing retro video games through my entertainment centre. Everything went well with the OS installation, but during that time, I had it plugged into my router via a wired connection. Once I moved the mini-PC into place in my sitting room and started using the wireless connection, I noticed that there was substantial lag even when doing something simple like typing in a terminal via SSH.
A quick ping test showed that there was clearly a problem with the wireless. For my home network, I consider any ping response time of >=5ms to be unacceptable and indicative of an underlying network problem:
$ ping -c 20 192.168.1.120
PING 192.168.1.120 (192.168.1.120) 56(84) bytes of data.
64 bytes from 192.168.1.120: icmp_seq=1 ttl=64 time=214 ms
64 bytes from 192.168.1.120: icmp_seq=2 ttl=64 time=30.7 ms
64 bytes from 192.168.1.120: icmp_seq=3 ttl=64 time=54.4 ms
64 bytes from 192.168.1.120: icmp_seq=4 ttl=64 time=75.1 ms
64 bytes from 192.168.1.120: icmp_seq=5 ttl=64 time=97.8 ms
64 bytes from 192.168.1.120: icmp_seq=6 ttl=64 time=122 ms
64 bytes from 192.168.1.120: icmp_seq=7 ttl=64 time=142 ms
64 bytes from 192.168.1.120: icmp_seq=8 ttl=64 time=2.46 ms
64 bytes from 192.168.1.120: icmp_seq=9 ttl=64 time=2.30 ms
64 bytes from 192.168.1.120: icmp_seq=10 ttl=64 time=4.72 ms
64 bytes from 192.168.1.120: icmp_seq=11 ttl=64 time=26.3 ms
64 bytes from 192.168.1.120: icmp_seq=12 ttl=64 time=2.30 ms
64 bytes from 192.168.1.120: icmp_seq=13 ttl=64 time=71.7 ms
64 bytes from 192.168.1.120: icmp_seq=14 ttl=64 time=94.6 ms
64 bytes from 192.168.1.120: icmp_seq=15 ttl=64 time=116 ms
64 bytes from 192.168.1.120: icmp_seq=16 ttl=64 time=139 ms
64 bytes from 192.168.1.120: icmp_seq=17 ttl=64 time=161 ms
64 bytes from 192.168.1.120: icmp_seq=18 ttl=64 time=184 ms
64 bytes from 192.168.1.120: icmp_seq=19 ttl=64 time=205 ms
64 bytes from 192.168.1.120: icmp_seq=20 ttl=64 time=23.5 ms
Though 4 of the 20 ping response times were under my 5-millisecond threshold, the other 16 were not only above it, but many of them were nonsensically high for a small home network (e.g. 214ms).
In this type of scenario, the first thing that I consider is the driver and/or firmware for the wireless adapter (namely, the Intel Wi-Fi 6 AX200). For nearly all modern Intel wireless chips the Linux driver is the in-kernel iwlwifi driver, so I didn’t pay too much attention there. That driver has two possible modules to use in conjunction:
- DVM (iwldvm)
- The module that supports the firmware for a specific group of (primarily) AGN chips
- MVM (iwlmvm)
- The module that supports the firmware for a much broader scope of Intel wireless chips
I had chosen the iwlmvm module and built it into my kernel for convenience. I also then chose to load the corresponding firmware directly into the kernel as well. The gigantic linux-firmware package contains all the various options for the iwlwifi supporting firmware, and from that table, I saw that the original firmware for the Intel Wi-Fi 6 AX200 was named:
iwlwifi-cc-46.3cfab8da.0.ucode
Looking at the current linux-firmware git tree (at the time of this writing), the relevant firmware packages were:
- iwlwifi-cc-a0-50.ucode
- iwlwifi-cc-a0-59.ucode
- iwlwifi-cc-a0-66.ucode
- iwlwifi-cc-a0-72.ucode
- iwlwifi-cc-a0-73.ucode
- iwlwifi-cc-a0-74.ucode
- iwlwifi-cc-a0-77.ucode
Using my trial-and-error approach of rebooting and looking at the output of dmesg | grep iwlwifi
to find the first version the kernel attempted (but failed) to load, I found that the correct version was iwl-cc-a0-72.ucode, and passed via the kernel’s firmware loader.
After trying various options (such as switching from wpa_supplicant to iwd, and using an older firmware blob), I finally found the fix for the latency and ping spikes: power saving. By issuing iw wlan0 set power_save off
, and then starting a new ping test, I could immediately see that the problem was fixed:
$ ping -c 20 192.168.1.120
PING 192.168.1.120 (192.168.1.120) 56(84) bytes of data.
64 bytes from 192.168.1.120: icmp_seq=1 ttl=64 time=2.90 ms
64 bytes from 192.168.1.120: icmp_seq=2 ttl=64 time=1.68 ms
64 bytes from 192.168.1.120: icmp_seq=3 ttl=64 time=2.43 ms
64 bytes from 192.168.1.120: icmp_seq=4 ttl=64 time=2.68 ms
64 bytes from 192.168.1.120: icmp_seq=5 ttl=64 time=3.08 ms
64 bytes from 192.168.1.120: icmp_seq=6 ttl=64 time=2.80 ms
64 bytes from 192.168.1.120: icmp_seq=7 ttl=64 time=3.25 ms
64 bytes from 192.168.1.120: icmp_seq=8 ttl=64 time=3.17 ms
64 bytes from 192.168.1.120: icmp_seq=9 ttl=64 time=2.83 ms
64 bytes from 192.168.1.120: icmp_seq=10 ttl=64 time=3.01 ms
64 bytes from 192.168.1.120: icmp_seq=11 ttl=64 time=2.77 ms
64 bytes from 192.168.1.120: icmp_seq=12 ttl=64 time=2.80 ms
64 bytes from 192.168.1.120: icmp_seq=13 ttl=64 time=3.37 ms
64 bytes from 192.168.1.120: icmp_seq=14 ttl=64 time=2.52 ms
64 bytes from 192.168.1.120: icmp_seq=15 ttl=64 time=2.71 ms
64 bytes from 192.168.1.120: icmp_seq=16 ttl=64 time=2.83 ms
64 bytes from 192.168.1.120: icmp_seq=17 ttl=64 time=3.25 ms
64 bytes from 192.168.1.120: icmp_seq=18 ttl=64 time=2.78 ms
64 bytes from 192.168.1.120: icmp_seq=19 ttl=64 time=2.48 ms
64 bytes from 192.168.1.120: icmp_seq=20 ttl=64 time=3.37 ms
--- 192.168.1.120 ping statistics ---
20 packets transmitted, 20 received, 0% packet loss, time 19036ms
rtt min/avg/max/mdev = 1.679/2.834/3.371/0.379 ms
Now that I had found the solution to the problem, the next task was to make the changes persistent across reboots. Of course, I could throw that iw
command command into an rc.local
script or something like that, but that seemed hackish to me. Instead, I decided to change my approach for loading the iwlwifi driver and firmware. Rather than having both the driver and the firmware built-in to the kernel, I chose to load them as kernel modules. Doing so allowed me to pass configuration options to the modules when they load. I made a configuration file at /etc/modprobe.d/iwlwifi.conf
with the following contents:
$ cat /etc/modprobe.d/iwlwifi.conf
## Has the same effect has running `iw wlan0 set power_save off`
## Both options sets are needed as iwlmvm will override iwlwifi :(
options iwlwifi power_save=0
## iwlmvm 1=always on, 2=balanced, 3=low-power
options iwlmvm power_scheme=1
As I mentioned in the comments there, BOTH options need to be set: one option passed to the iwlwifi module and the other option passed to the iwlmvm module. Also note the available parameters for the iwlmvm power_scheme
:
- 1 = always on
- 2 = balanced
- 3 = low-power
I have validated that these settings work across reboots, and that I no longer see the latency or ping spikes when connecting to this mini-PC over the Intel Wi-Fi 6 AX200. Some of these instructions may be specific to Gentoo and/or the OpenRC init system that I choose to use, but they should be readily adaptable to other distributions and init systems.
Cheers,
Nathan Zachary