Skip to content

Commit 51d0656

Browse files
l1kKAGA-KOKO
authored andcommitted
genirq/manage: Reduce priority of forced secondary interrupt handler
Crystal reports that the PCIe Advanced Error Reporting driver gets stuck in an infinite loop on PREEMPT_RT: Both the primary interrupt handler aer_irq() as well as the secondary handler aer_isr() are forced into threads with identical priority. Crystal writes that on the ARM system in question, the primary handler has to clear an error in the Root Error Status register... "before the next error happens, or else the hardware will set the Multiple ERR_COR Received bit. If that bit is set, then aer_isr() can't rely on the Error Source Identification register, so it scans through all devices looking for errors -- and for some reason, on this system, accessing the AER registers (or any Config Space above 0x400, even though there are capabilities located there) generates an Unsupported Request Error (but returns valid data). Since this happens more than once, without aer_irq() preempting, it causes another multi error and we get stuck in a loop." The issue does not show on non-PREEMPT_RT because the primary handler runs in hardirq context and thus can preempt the threaded secondary handler, clear the Root Error Status register and prevent the secondary handler from getting stuck. Emulate the same behavior on PREEMPT_RT by assigning a lower default priority to the secondary handler if the primary handler is forced into a thread. Reported-by: Crystal Wood <crwood@redhat.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Crystal Wood <crwood@redhat.com> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Link: https://patch.msgid.link/f6dcdb41be2694886b8dbf4fe7b3ab89e9d5114c.1761569303.git.lukas@wunner.de Closes: https://lore.kernel.org/r/20250902224441.368483-1-crwood@redhat.com/
1 parent 9ea2b81 commit 51d0656

3 files changed

Lines changed: 18 additions & 1 deletion

File tree

include/linux/sched.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,7 @@ extern int sched_setscheduler(struct task_struct *, int, const struct sched_para
19011901
extern int sched_setscheduler_nocheck(struct task_struct *, int, const struct sched_param *);
19021902
extern void sched_set_fifo(struct task_struct *p);
19031903
extern void sched_set_fifo_low(struct task_struct *p);
1904+
extern void sched_set_fifo_secondary(struct task_struct *p);
19041905
extern void sched_set_normal(struct task_struct *p, int nice);
19051906
extern int sched_setattr(struct task_struct *, const struct sched_attr *);
19061907
extern int sched_setattr_nocheck(struct task_struct *, const struct sched_attr *);

kernel/irq/manage.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,10 @@ static int irq_thread(void *data)
12391239

12401240
irq_thread_set_ready(desc, action);
12411241

1242-
sched_set_fifo(current);
1242+
if (action->handler == irq_forced_secondary_handler)
1243+
sched_set_fifo_secondary(current);
1244+
else
1245+
sched_set_fifo(current);
12431246

12441247
if (force_irqthreads() && test_bit(IRQTF_FORCED_THREAD,
12451248
&action->thread_flags))

kernel/sched/syscalls.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,19 @@ void sched_set_fifo_low(struct task_struct *p)
856856
}
857857
EXPORT_SYMBOL_GPL(sched_set_fifo_low);
858858

859+
/*
860+
* Used when the primary interrupt handler is forced into a thread, in addition
861+
* to the (always threaded) secondary handler. The secondary handler gets a
862+
* slightly lower priority so that the primary handler can preempt it, thereby
863+
* emulating the behavior of a non-PREEMPT_RT system where the primary handler
864+
* runs in hard interrupt context.
865+
*/
866+
void sched_set_fifo_secondary(struct task_struct *p)
867+
{
868+
struct sched_param sp = { .sched_priority = MAX_RT_PRIO / 2 - 1 };
869+
WARN_ON_ONCE(sched_setscheduler_nocheck(p, SCHED_FIFO, &sp) != 0);
870+
}
871+
859872
void sched_set_normal(struct task_struct *p, int nice)
860873
{
861874
struct sched_attr attr = {

0 commit comments

Comments
 (0)