WiFi 6 is not faster than Ethernet on the Raspberry Pi

I didn't know it at the time, but my results testing the EDUP WiFi 6 card (which uses the Intel AX200 chipset) on the Raspberry Pi in December weren't accurate.

It doesn't get 1.34 gigabits of bandwidth with the Raspberry Pi Compute Module 4 like I stated in my December video, WiFi 6 on the Raspberry Pi CM4 makes it Fly!.

I'm very thorough in my benchmarking, and if there's ever a weird anomaly, I try everything I can to prove or disprove the result before sharing it with anyone.

In this case, since I was chomping at the bit to move on to testing a Rosewill 2.5 gigabit Ethernet card, I didn't spend as much time as I should have re-verifying my results.

MZHOU WiFi Bluetooth M.2 NGFF Adapter Card for PCIe Raspberry Pi Compute Module 4 AX200 Intel 6

In this post I'll describe how testing this $20 M.2 WiFi adapter card suggested by Javier Choclin led to me learning a lot about Linux's wireless networking stack.

Video for this post

There is a video that goes along with this blog post, for the visually-inclined:

It's just not possible!

Full transparency, what really got me started thinking about my performance results was this comment on YouTube, which reads:

Intel ax200 is a 2x2 chip. Max 11ax Phy datarate it can support is MCS11 (1024-QAM) which is 1201Mbps. It is impossible to get 1340 Mbps iperf throughput using a 2x2 chipset. Can you share the iperf log showing 1340Mbps like you showed for ethernet :)

That 1.2 gigabit limit is definitely lower than the 1.34 gigabit result I got in my testing.

So what gives?

Well, let's start with the facts:

  • A couple months ago, when I tested five Ethernet interfaces at the same time on the Intel I340-T4 card, I put each interface on its own network, connecting just that interface to an individual Raspberry Pi on the same network. I got 4.15 Gbps with the four interfaces on that card and the internal network interface using Jumbo Frames.
  • When I tested the EDUP WiFi 6 card, I got 930 Mbps of one-way throughput.
  • When I tested an ASUS 10 Gbps card (more on it coming soon!), I got 3.26 Gbps of one-way throughput.
  • When I tested an Intel AX200 Desktop Kit in the MZHOU adapter, I got 930 Mbps of one-way throughput.
  • Then I tested it again, though I had the Pi disconnected from it's wired network connection, and I only got 600-800 Mbps of one-way throughput.

Compute Module 4 Network Adapter Speed Benchmark Results

In hindsight, the problem is more obvious—getting 930 Mbps for all the wireless tests when Ethernet was connected should've clued me in earlier. But getting to the point where I could prove what I thought might've happened actually happened took some time.

Testing the MZHOU WiFi/Bluetooth M.2 NGFF adapter

To quickly review the card that I tested for this post—if you want to adapt an M.2 WiFi and bluetooth module you have laying around, maybe from an old broken down laptop or desktop computer, this little MZHOU adapter I tested works a treat!

With that out of the way... let's get back to the substance of this post.

Strange behaviors in the Linux networking stack

Anyways, getting back to the problem I uncovered, I found it odd that both of my WiFi tests got about 930 Mbps when I tested them the first time.

I was using iperf3's --bind option, which according to the documentation:

binds to the interface associated with the address

So if I have two interfaces, let's say eth0 on, and wlan0 on, and I wanted to test the wlan0 interface, I could just use --bind, right?

Well, no, actually.

Linux's kernel networking stack is optimized for getting packets routed in the most efficient manner. Usually, that's a great thing!

But the problem was that the Linux kernel saw that both my wired network and wifi connections were operating on the same network, so it optimized the packet flow for me by routing data through the wired ethernet connection, even though I told iperf3 to bind to the wifi interface.

Long story short, I am not the first person to run into this issue, and I found out about a couple things that I could try to combat the problem, like adjusting the arp_filter setting to disable the kernel's intelligent routing, or segregating all my network interfaces on their own subnets.

But the easiest thing, in the end, was to disable the network interface I wasn't testing. So before I re-ran my benchmarks, I ran sudo ip link set eth0 down to disable the onboard gigabit ethernet.

And after doing that, and running more benchmarks, I have to agree with marvell marvell: it is, in fact, impossible to get more than 1.2 gigabits of throughput with the Intel AX200.

Intel AX200 WiFi Performance Benchmarks Raspberry Pi Compute Module 4

My benchmarking showed that throughput reached around 800 Mbps from the Pi to the router, and 1.1 Gbps from the router to the Pi.

Those results are still excellent, and beat any older WiFi device I have in my house right now—including my $3000 MacBook Pro—but my conclusion from the last video that "WiFi is faster than Ethernet" on this Pi isn't entirely true.

Before I move on from that topic, I should mention the maintainers of iperf3 have been trying to make benchmarking work better with multiple devices on the same network, and there's a bleeding edge option I tested called --bind-dev, but unfortunately it didn't make a difference on the Raspberry Pi.

Learning about wpa_supplicant

In the middle of all this benchmarking, I decided to also dig in and learn more about wpa_supplicant that we use to control WiFi on our Raspberry Pis, or in Debian in general.

I noticed in my testing that if I had both the internal WiFi interface (wlan0) and the PCIe interface (wlan1) enabled, wpa_supplicant would always choose the external interface. And usually that was what I wanted, so I didn't question it. But why did it always choose the external interface?

I realize the number of people with multiple WiFi interfaces on their computers is probably very tiny, but still, the wpa_supplicant documentation shows nothing about how to specify an interface for a configuration during startup—only how to specify an interface when invoking wpa_supplicant directly.

Well, after wondering this a long time, inspiration finally struck me after I saw this answer from Hannes on Stack Exchange. His post reminded me of the obvious fact: I'm using Linux. I can figure this out myself!

First I tried finding the wpa_supplicant codebase and documentation, and found the docs through this website... but a lot of the links were either broken or somewhat unhelpful. I couldn't even find a clone of the source on GitHub (though I didn't look too hard). So I switched gears and had more luck searching for the code behind dhcpcd, which took me to Roy Marple's website (https://roy.marples.name). His site has links to the official code repository, as well as a GitHub mirror that's easier to browse.

Looking around in the code, I noticed the files that load wpa_supplicant on the Raspberry Pi are in the hooks folder. Specifically, the source for the wpa_supplicant hook is in this file.

Right at the top, it looks like there's my answer: there's a bash for loop that looks for a given set of wpa_supplicant files, starting with a file named after the interface:

    for x in \
        /etc/wpa_supplicant/wpa_supplicant-"$interface".conf \
        /etc/wpa_supplicant/wpa_supplicant.conf \
        /etc/wpa_supplicant-"$interface".conf \
        /etc/wpa_supplicant.conf \
    ; do

If it doesn't find a file with the interface name in it, it goes to the plain config file. If it doesn't find either, it goes up a directory, into et-see, and searches for the same files.

There's still more digging I could do—but my curiosity was satisfied for the time being, and I was itching to get back to some testing with 10 Gbps networking gear.

More testing with the MZHOU and Coral.ai

Getting back to the original topic of my post, the MZHOU adapter card: I'm planning on testing a Google Coral.ai TPU with this card, and that's the main reason I bought it in the first place.

But it's just a happy accident that I learned a ton about WiFi and Linux networking in the process of testing this boring little adapter!


Hello Jeff, I am glad to know that you finally found the anomaly in the testing. 930Mbps with Wi-Fi 6 seems at about max it can give you in usual conditions. With the most ideal conditions line no interference, probably cabled up antennae, theoretically, with a phy rate of 1201Mbps, one may be able to hit max UDP throughput is 985Mbps for 64 AMPDU aggregation size (indicated by max block ack bitmap) and 4K AMSDU (2*1500 MSDU) in a MPDU. But again that too would be contingent to Intel's SW stack being optimized for peak 11ax TP. Those last 50Mbps are really tough to achieve :)

Hi Jeff, thanks for your detailed tutorials and testings on CM4. I experiment with PI4 on astrophotography since over 2 years now and I became very excited when I heard about PCI support on CM4. I ordered a Percicom 3-lane PCI Switch with some PCI cards and also AX200 PCI Card to Test with my Ubuntu 21.04 installation. At 1st tests all cards have been identified on the next day the intel wifi6 AX200 was no longer addressed within PCI bus. I tested the card on my pc w10/Linux and it was not recognised any more. I replaced the AX200 Module on the pci Adapter card with another one which I knew it was working fine and the same happend again, the AX200 module is no longer recognised on my pc w10/Linux machine. For me , my CM4 turned out to have become a wifi6 card destroyer 😂