-
Notifications
You must be signed in to change notification settings - Fork 5.4k
PREEMPT_RT: RP1 GPIO IRQ thread does not follow smp_affinity — PF_NO_SETAFFINITY prevents userspace workaround #7301
Description
Describe the bug
On a PREEMPT_RT kernel (rpi-7.0.y), writing to /proc/irq/167/smp_affinity_list for an RP1 GPIO interrupt (pps-gpio on GPIO12) correctly steers the hardware interrupt to the target CPU, but the force-threaded IRQ handler (irq/167-pps@12.-1) remains pinned to whichever CPU it was initially scheduled on. The thread has PF_NO_SETAFFINITY set, so taskset, cset, and cpuset cgroup attachment all fail, leaving no userspace workaround.
This is related to #6077 (missing irq_set_affinity in rp1_irq_chip) and #7275 (effective affinity mask not updated), but is a distinct problem: the IRQ's effective affinity appears correct, interrupts land on the right CPU, yet the threaded handler does not follow.
Root cause analysis
The interrupt chain for an RP1 GPIO IRQ is:
GPIO edge → pinctrl-rp1 (rp1_gpio_irq_chip) → rp1 MFD (rp1_irq_chip) → PCIe MSI
rp1_gpio_irq_chip in drivers/pinctrl/pinctrl-rp1.c delegates affinity to its parent:
cif (parent_data && parent_data->chip->irq_set_affinity)
return parent_data->chip->irq_set_affinity(parent_data, dest, force);
return -EINVAL;
For the kernel to propagate affinity to the IRQ thread (irq_set_thread_affinity()), irq_do_set_affinity() in kernel/irq/manage.c requires the chip callback to return IRQ_SET_MASK_OK or IRQ_SET_MASK_OK_DONE and update the effective affinity mask via irq_data_update_effective_affinity(). The rp1_irq_chip in drivers/mfd/rp1.c either:
- Still lacks
irq_set_affinity(the patch from Can't set CPU affinity on RP1 downstream interrupts #6077 was proposed but may not be merged in rpi-7.0.y), causing the delegation chain to return -EINVAL, or - Has it but doesn't call
irq_data_update_effective_affinity(), which triggers the warning from Devicestree issue on kernel 6.18.x #7275:genirq: irq_chip rp1_irq_chipdid not update eff. affinity mask of irq 167
In either case, irq_set_thread_affinity() is never called, and the IRQ thread stays on its original CPU.
On a non-RT kernel this is invisible because the handler runs in hardirq context. On PREEMPT_RT, where all IRQ handlers are force-threaded, this means the PPS timestamp is captured on a potentially contended CPU rather than the isolated one — directly degrading timing precision.
Steps to reproduce the behaviour
Boot a Pi 5 with a PREEMPT_RT kernel from rpi-7.0.y
Attach a PPS source to GPIO12 (or any RP1 GPIO)
Set IRQ affinity:
bashecho 3 > /proc/irq/167/smp_affinity_list
cat /proc/irq/167/effective_affinity_list # shows 3
Observe that interrupts land on CPU3:
bashgrep 167 /proc/interrupts # CPU3 column increments
Observe that the thread is NOT on CPU3:
bashps -eo pid,psr,comm | grep pps
# 139 1 irq/167-pps@12.-1
Attempt to move the thread — all fail:
bashtaskset -p 0x8 139
# taskset: affinity cannot be set due to PF_NO_SETAFFINITY flag set
cset proc -m -k -p 139 -t user
# **> 1 tasks are not movable, impossible to move
Impact
On a PREEMPT_RT time server, the PPS interrupt fires on the isolated CPU but the timestamp is captured on a different, non-isolated CPU. Every PPS pulse pays a cross-CPU wakeup penalty, adding variable latency to the timing path. This is a significant problem for precision NTP/PTP time servers using the Pi 5.
Expected behaviour
Writing to smp_affinity_list should cause both the hardware interrupt AND the threaded handler to run on the specified CPU, as it does for all other IRQ chips with proper irq_set_affinity implementations.
Suggested fix
Ensure rp1_irq_chip.irq_set_affinity in drivers/mfd/rp1.c:
- Exists (merge the patch from Can't set CPU affinity on RP1 downstream interrupts #6077 if not already present)
- Calls
irq_data_update_effective_affinity(irqd, dest)before returning - Returns
IRQ_SET_MASK_OKrather than delegating the return value without updating effective affinity
This will cause irq_do_set_affinity() to call irq_set_thread_affinity(), which propagates the affinity to the force-threaded handler.
Device (s)
Raspberry Pi 5
System
OS: Raspberry Pi reference 2021-10-30 – Generated using pi-gen, https://github.com/RPi-Distro/pi-gen, c12b1df4ed6416fb0df33ba1731c5b13c1bdbdf8, stage2
Firmware: 2026/03/02 13:39:08 – Copyright (c) 2012 Broadcom – version bef7b468 (release) (embedded)
Kernel: Linux chronos 7.0.0-rc6-v8-16k-NTP+ #8 SMP PREEMPT_RT Tue Mar 31 11:07:24 CEST 2026 aarch64 GNU/Linux
PPS source: u-blox ZED-F9P on HAT, PPS on GPIO12 via pps-gpio
Kernel cmdline includes: isolcpus=3 nohz_full=3 rcu_nocbs=3 irqaffinity=0-2 pcie_aspm=off
Logs
No response
Additional context
# Interrupt lands on CPU3 correctly:
IRQ 167: cpus=[3] 94 0 0 528 pinctrl-rp1 18 Edge pps@12.-1
# But thread runs on CPU1:
ps -eo pid,cls,rtprio,psr,comm | grep pps
139 FF 99 1 irq/167-pps@12.-1
# Thread affinity is wide open (not locked to CPU3):
taskset -p 139
pid 139's current affinity mask: f
# But PF_NO_SETAFFINITY blocks all attempts to change it