packet steering and bidirectional forwarding on ipq807x (was: Re: [PATCH] qualcommax: qca_edma: do not set a zero RX hash)
John Macias
dencity7 at gmail.com
Sat Oct 24 23:55:48 PDT 2026
Hello,
A follow-up with two corrections to my last message and the causes I
found since. Same RAX120 v1 and test as before.
1. Corrections
- Which NAPI thread serves which ring. packet-steering.uc pairs the
NAPI threads with rx queues in PID order. Here the PIDs run in reverse
ring order: the GRO tracepoint shows napi/eth0-5 receiving ring 0 and
napi/eth0-8 receiving ring 3, but eth0-8 has the lowest PID and is
pinned as if it served rx-0. So main's default is not "RPS to the
next CPU" on this device. Rings 1 and 3 send RPS to their own NAPI
CPU, and rings 0 and 2 send it to each other's. I described the rule
as intended, not as it ran.
- Which pairs share a ring. The ring a flow lands on changes from boot
to boot (I assume the RSS seed is random), so the client ports I named
are not fixed. One or two of the eight pairs collided on each boot.
The reverse order looks generic rather than driver-specific:
netif_napi_dev_list_add() puts a new NAPI (with no ID yet) at the head
of dev->napi_list, and netif_set_threaded() creates the threads by
walking that list. A driver that adds its NAPIs in ring order and then
enables threading gets threads with PIDs in reverse ring order. Sorting
by the NAPI ID in the thread name (napi/<dev>-<id>) gives ring order
for such drivers; section 4 shows what that does here. qca_edma does
not call netif_queue_set_napi(), so netlink cannot report the
queue-to-NAPI link on this device.
2. Causes
a) RPS on the DSA user ports is ignored for conduit rings 1-3.
qca_edma records the RX ring with skb_record_rx_queue(). DSA passes
the skb to lan1/wan with queue_mapping unchanged. The user ports
have a single RX queue, so get_rps_cpu() sees an index past
real_num_rx_queues and skips RPS without a warning. That is why
setting rps_cpus on lan1 and wan behaved like RPS off. Clearing
queue_mapping in dsa_switch_rcv() fixes it here. I will send that to
netdev separately.
b) Each EDMA NAPI thread serves RX ring q and TX-completion ring q, and
XPS maps CPU q to tx-q. I first tried rings 0-1 on CPU0, rings 2-3 on
CPU1 and forwarding on CPU2/3, so all TX went to rings 2-3. A pair
that hashed to rings 2-3 then put RX and TX completion for both
directions on CPU1, and lost 40-48% in all 6 runs on that boot.
3. A layout that works
This is the ingress-port layout from our older single-ring build,
expressed with main's settings and applied as a platform
packet-steering override:
ethtool -X eth0 weight 1 0 0 0 all RX on ring 0, NAPI on CPU0
rings 2-3 NAPI and IRQs on CPU1 TX completion (via XPS below)
xps_cpus tx-q = CPU q (main's existing setting)
eth0 rps_cpus 0, lan* rps_cpus 4, wan rps_cpus 8
With only ring 0 in use, the recorded queue is always 0, so this does
not depend on the DSA fix. Results, worse direction, 8 flow pairs; the
first two rows are from the same boot:
main default steering 0.00-55.9% [1]
this layout 0.00-0.02%
this layout, after a clean reboot 0.00-0.66%
[1] 4 of the 8 pairs lost 17.7-55.9%.
With the DSA fix, RSS over rings 0-1 (both on CPU0) also gave
0.00-0.03% over 11 runs. Other traffic was unchanged: UDP 900 Mb/s one
way 0.00%, UDP 4 x 225 Mb/s each way 0.10/0.14%, TCP 940 Mb/s each
way, and TCP bidirectional 928 + 795 Mb/s against 814 + 928 Mb/s with
the default steering in the same boot.
4. The next-CPU rule with the order corrected
I changed packet-steering.uc to sort the NAPI threads by NAPI ID, so
thread q runs on CPU q and ring q's RPS goes to CPU q+1 as intended.
I ran it against the stock script in the same boot, on two boots, 8
pairs each:
pairs above 25% worst
stock script (PID order) 9 of 16 46.2%
NAPI ID order 9 of 16 95.1%
layout in section 3 [2] 0 of 16 0.66%
[2] the same 8 pairs on two other boots.
The 95% cases are pairs on one ring. Both directions are then
forwarded on CPU q+1, and XPS sends the forwarded packets to TX ring
q+1, whose completions run on that same CPU next to ring q+1's RX. So
on this device the order is not what causes the loss, and correcting
it alone makes the worst case worse. I would not send that change by
itself.
5. Questions
Felix, would you rather have the rule leave RPS off when a device
already has one RSS ring per CPU, or keep it and expect targets like
this one to use a platform override? I can test either here.
Julius, putting all RX on one ring gives up RSS. It fits a router whose
traffic all comes in through 1 Gb/s ports, but it is not a general
answer. Do you see a better way to keep RX and TX completion for the
same flows off one CPU, for example separate NAPI instances for the
TX-completion rings?
The work was done with AI assistance.
Thanks,
John
More information about the openwrt-devel
mailing list