WiFi 6 on Jetson Xavier NX

Posted by shengliangd's blog on December 30, 2022

I want to use a WiFi 6 USB adapter (CF-953AX) on Jetson Xavier NX. This is quite tricky because the kernel driver (mt7921u) is available only after Linux 5.19, while the newest Jetson SDK comes with 5.10.104-tegra. A straight thought would be to upgrade the Linux kernel on Jetson board. However, that would require a lot of customization and kernel patching, which is tedious and error-prone. Then I tried to look for backport of mt7921u driver, but no result.

Then one idea comes to my mind: why not just drive the USB device in a VM, and bridge the network to the host? There will be no risk of messing up the host kernel, and I will be free to use newest Linux kernel. It is definitely worth a try, but finally I could only get <20Mbps speed, and haven’t found the root cause yet. In the end I decide to backport the mt7921u driver from Linux 5.19, described in my next blog. Check out my repo if you need it.

Although the VM solution is not perfect, it is still an interesting idea, and the steps are shared in this post.

Setup the VM

I downloaded Ubuntu 22.10 server version for arm64, which comes with Linux 5.19. To ease the setup, I used virt-manager. I believe there is no need to remind the readers about how to setup the VM with USB passthrough, just tweak in the virt-manager UI, one can easily find these settings.

Unfortunately, a trial booting gives error message about PMU:

kvm-pmu-error

Obviously, Jetpack 5 kernel comes with KVM support (before Jetpack 5, you need to manually add the support, check this). It is related to PMU. Some googling finds this.

To be brief, there are some commits about PMU in Linux 5.11, some of them are already ported back to 5.10 while others are not. Just so bad luck… Well, a simple skim on what has not been ported stops me from trying backporting it. Let’s just do it in another direction, namely remove the backported commit:

kvm-patch

As the commit message implies, it is not critical. After manually removing this patch, I compiled and replaced the Linux kernel (see this), and reboot. Then the VM starts, and the USB WiFi adapter is recognized in the VM. Unfortunately, a quick test with iperf3 only got around 18Mbps speed, which is much slower than others’ test(~500Mbps). Let’s first make the host able to access the WiFi, then debug the speed issue.

Setup Routing

Now, the VM is up and running with a USB WiFi adapter. But how do we access the WiFi from the host?

First, make sure the host and the VM can access each other. This can be done by setting up a bridged network. Then, set up routing on the host so that access to WiFi ips from the host goes to the VM. Finally, set up NAT on the VM with the following commands:

1
2
3
4
5
# enable ip forwarding
sysctl -w net.ipv4.ip_forward=1
iptables --policy FORWARD ACCEPT
# set up NAT
iptables -t nat -A POSTROUTING -o <vm_wnic> -j MASQUERADE

However, in this way, the host hides behind NAT and cannot be accessed by devices in WiFi. We actually would like to make the VM just like a driver, or to be more specific, make the WiFi IP of the VM behave like an IP of the host. First of all, ip forwarding in the VM is still necessary:

1
2
sysctl -w net.ipv4.ip_forward=1
iptables --policy FORWARD ACCEPT

Then, on the VM, all output packets to WiFi subnet should rewrite its source IP to the VM’s WiFi IP, such that the remote host response to the VM’s WiFi IP instead of the host’s IP:

1
iptables -t nat -A POSTROUTING -o <vm_wnic> -j SNAT --to-source <vm_wifi_ip>

Still, on the VM, all input packets from WiFi should be rewritten to the host’s IP:

1
iptables -t nat -A PREROUTING -i <vm_wnic> -j DNAT --to-destination <host_ip>

Now the WiFi IP of the VM behaves like an IP of the host.