]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
qualcommax: pin EDMA processing to dedicated CPUs 24203/head
authorRobert Marko <robimarko@gmail.com>
Mon, 13 Jul 2026 09:07:26 +0000 (11:07 +0200)
committerRobert Marko <robimarko@gmail.com>
Mon, 13 Jul 2026 10:34:33 +0000 (12:34 +0200)
IPQ60xx and IPQ807x use threaded NAPI for EDMA. The NAPI threads
inherit CPU0 affinity, and running both there can eventually starve the
RX fill ring under sustained traffic.

Add an IPQ60xx smp_affinity service and extend the existing IPQ807x
service to discover the EDMA IRQs and NAPI IDs at runtime. Place RX on
CPU1 and TX on CPU2. Retry EDMA setup asynchronously because its NAPI
threads may appear after the init service runs.

Provide the same UCI enable and logging controls on both subtargets.
IPQ50xx uses a different Ethernet driver and is left unchanged.

Link: https://github.com/openwrt/openwrt/pull/24203
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/qualcommax/ipq60xx/base-files/etc/init.d/smp_affinity [new file with mode: 0755]
target/linux/qualcommax/ipq60xx/base-files/etc/uci-defaults/15_smp_affinity [new file with mode: 0644]
target/linux/qualcommax/ipq807x/base-files/etc/init.d/smp_affinity

diff --git a/target/linux/qualcommax/ipq60xx/base-files/etc/init.d/smp_affinity b/target/linux/qualcommax/ipq60xx/base-files/etc/init.d/smp_affinity
new file mode 100755 (executable)
index 0000000..45868e9
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh /etc/rc.common
+
+START=94
+
+PROG=smp_affinity
+
+log_msg() {
+       local type="$1" id="$2" name="$3" affinity="$4"
+
+       logger -t "$PROG" "Pinning $type($id) $name to CPU $affinity"
+}
+
+get_irq() {
+       awk -v name="$1" '$NF == name {
+               sub(/:$/, "", $1)
+               print $1
+               exit
+       }' /proc/interrupts
+}
+
+get_napi_threads() {
+       local comm comm_path id pid
+
+       TX_NAPI_ID=
+       TX_PID=
+       RX_NAPI_ID=
+       RX_PID=
+
+       for comm_path in /proc/[0-9]*/comm; do
+               read -r comm < "$comm_path"
+               case "$comm" in
+               napi/eth0-[0-9]*)
+                       id="${comm##*-}"
+                       pid="${comm_path#/proc/}"
+                       pid="${pid%/comm}"
+
+                       if [ -z "$TX_NAPI_ID" ] || [ "$id" -lt "$TX_NAPI_ID" ]; then
+                               TX_NAPI_ID="$id"
+                               TX_PID="$pid"
+                       fi
+
+                       if [ -z "$RX_NAPI_ID" ] || [ "$id" -gt "$RX_NAPI_ID" ]; then
+                               RX_NAPI_ID="$id"
+                               RX_PID="$pid"
+                       fi
+                       ;;
+               esac
+       done
+}
+
+pin_irq() {
+       local irq="$1" mask="$2" affinity="$3" name="$4"
+
+       [ -n "$irq" ] || return
+       $enable_log && log_msg IRQ "$irq" "$name" "$affinity"
+       echo "$mask" > "/proc/irq/$irq/smp_affinity"
+}
+
+pin_thread() {
+       local pid="$1" mask="$2" affinity="$3" name="$4"
+
+       [ -n "$pid" ] || return
+       $enable_log && log_msg thread "$pid" "$name" "$affinity"
+       taskset -p "$mask" "$pid" >/dev/null
+}
+
+enable_affinity_edma() {
+       local seconds=0 rx_irq tx_irq
+
+       while [ "$seconds" -lt 60 ]; do
+               rx_irq="$(get_irq edma_rxdesc)"
+               tx_irq="$(get_irq edma_txcmpl)"
+               get_napi_threads
+
+               [ -n "$rx_irq" ] && [ -n "$tx_irq" ] &&
+                       [ -n "$RX_PID" ] && [ -n "$TX_PID" ] &&
+                       [ "$RX_PID" != "$TX_PID" ] && break
+
+               seconds=$((seconds + 1))
+               sleep 1
+       done
+
+       [ -n "$rx_irq" ] && [ -n "$tx_irq" ] &&
+               [ -n "$RX_PID" ] && [ -n "$TX_PID" ] &&
+               [ "$RX_PID" != "$TX_PID" ] || return
+
+       pin_irq "$rx_irq" 2 1 edma_rxdesc
+       pin_thread "$RX_PID" 2 1 "EDMA RX NAPI"
+       pin_irq "$tx_irq" 4 2 edma_txcmpl
+       pin_thread "$TX_PID" 4 2 "EDMA TX NAPI"
+}
+
+boot() {
+       local enable
+
+       config_load smp_affinity
+
+       config_get_bool enable "general" enable 1
+       config_get_bool enable_log "general" enable_log 1
+
+       [ "$enable" -eq 1 ] && enable=true || enable=false
+       [ "$enable_log" -eq 1 ] && enable_log=true || enable_log=false
+
+       $enable && enable_affinity_edma &
+}
diff --git a/target/linux/qualcommax/ipq60xx/base-files/etc/uci-defaults/15_smp_affinity b/target/linux/qualcommax/ipq60xx/base-files/etc/uci-defaults/15_smp_affinity
new file mode 100644 (file)
index 0000000..3ec0adc
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+uci -q get smp_affinity && exit 0
+touch /etc/config/smp_affinity
+
+uci -q batch << EOF
+  set smp_affinity.general=smp_affinity
+  set smp_affinity.general.enable='1'
+  set smp_affinity.general.enable_log='1'
+  commit smp_affinity
+EOF
index ecf88aea09581469268cb4632777f855fcfdb96f..690a8cfa34f7a31627c3b4e1bb97d55a33c2f452 100755 (executable)
@@ -81,6 +81,68 @@ set_affinity() {
     done
 }
 
+get_napi_threads() {
+
+  local comm comm_path id pid
+
+  TX_NAPI_ID=
+  TX_PID=
+  RX_NAPI_ID=
+  RX_PID=
+
+  for comm_path in /proc/[0-9]*/comm; do
+    read -r comm < "$comm_path"
+    case "$comm" in
+      napi/eth0-[0-9]*)
+        id="${comm##*-}"
+        pid="${comm_path#/proc/}"
+        pid="${pid%/comm}"
+
+        if [ -z "$TX_NAPI_ID" ] || [ "$id" -lt "$TX_NAPI_ID" ]; then
+          TX_NAPI_ID="$id"
+          TX_PID="$pid"
+        fi
+
+        if [ -z "$RX_NAPI_ID" ] || [ "$id" -gt "$RX_NAPI_ID" ]; then
+          RX_NAPI_ID="$id"
+          RX_PID="$pid"
+        fi
+        ;;
+    esac
+  done
+}
+
+set_thread_affinity() {
+
+  local pid="$1" affinity="$2" bitmask
+
+  [ -n "$pid" ] || return
+  bitmask=$(cpus_to_bitmask "$affinity") &&
+    taskset -p "$bitmask" "$pid" >/dev/null
+}
+
+enable_affinity_edma() {
+
+  local seconds=0
+
+  while [ "$seconds" -lt 60 ]; do
+    get_napi_threads
+    [ -n "$RX_PID" ] && [ -n "$TX_PID" ] &&
+      [ "$RX_PID" != "$TX_PID" ] && break
+
+    seconds=$((seconds + 1))
+    sleep 1
+  done
+
+  [ -n "$RX_PID" ] && [ -n "$TX_PID" ] &&
+    [ "$RX_PID" != "$TX_PID" ] || return
+
+  set_affinity 'edma_rxdesc' 1
+  set_thread_affinity "$RX_PID" 1
+  set_affinity 'edma_txcmpl' 2
+  set_thread_affinity "$TX_PID" 2
+}
+
 enable_affinity_ipq807x() {
 
   # assign 4 rx interrupts to each core
@@ -99,11 +161,6 @@ enable_affinity_ipq807x() {
   set_affinity 'ppdu-end-interrupts-mac2'      2
   set_affinity 'ppdu-end-interrupts-mac3'      3
 
-  # assign 4 lan/wan to core 4
-  set_affinity 'edma_txcmpl'                   3
-  set_affinity 'edma_rxfill'                   3
-  set_affinity 'edma_rxdesc'                   3
-  set_affinity 'edma_misc'                     3
 }
 
 boot() {
@@ -118,5 +175,8 @@ boot() {
   [ "$enable"     -eq 1 ] && enable=true     || enable=false
   [ "$enable_log" -eq 1 ] && enable_log=true || enable_log=false
 
-  $enable && enable_affinity_ipq807x
+  $enable && {
+    enable_affinity_ipq807x
+    enable_affinity_edma &
+  }
 }