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