Skip to content

PREEMPT_RT: RP1 GPIO IRQ thread does not follow smp_affinity — PF_NO_SETAFFINITY prevents userspace workaround #7301

@by

Description

@by

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:

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:

  1. Exists (merge the patch from Can't set CPU affinity on RP1 downstream interrupts #6077 if not already present)
  2. Calls irq_data_update_effective_affinity(irqd, dest) before returning
  3. Returns IRQ_SET_MASK_OK rather 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions