Re: [PATCH dovetail] irq_pipeline: synchronize inband IRQ log in exit_to_user_mode_prepare()

Tobias Schaffner <[email protected]>
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
Hi Florian,

On 2/16/26 10:27, Florian Bezdeka wrote:
> On Sun, 2026-02-15 at 00:25 +0100, Florian Bezdeka wrote:
>> On 2/14/26 12:39, Tobias Schaffner wrote:
>>> Architectures using the generic handle_irq_pipelined() path do not
>>
>> arm64 and x86 are not using that, so the only arch left is arm.
>> See below...
>>
>>> synchronize the inband IRQ log in handle_irq_pipelined_finish()
>>> because inband is still stalled from irqentry_enter() when it gets
>>> called.
>>
>> I think the real issue / correct wording is:
>>
>> _TIF_WORK is left pending on IRQ exit.
>>
>>>
>>> As a result, deferred inband IRQs may not be played before the kernel
>>> checks for pending work in exit_to_user_mode_loop(), causing
>>> those IRQs to be missed on the return path to userspace.
>>>
>>> Flush the inband IRQ log in exit_to_user_mode_prepare() before
>>> entering the work loop.
>>>
>>> Signed-off-by: Tobias Schaffner <[email protected]>
>>> ---
>>>   include/linux/irq-entry-common.h | 6 ++++++
>>>   1 file changed, 6 insertions(+)
>>>
>>> diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h
>>> index b21fb5893a67..e5815511a80a 100644
>>> --- a/include/linux/irq-entry-common.h
>>> +++ b/include/linux/irq-entry-common.h
>>> @@ -8,6 +8,7 @@
>>>   #include <linux/tick.h>
>>>   #include <linux/kmsan.h>
>>>   #include <linux/unwind_deferred.h>
>>> +#include <linux/irq_pipeline.h>
>>>   
>>>   #include <asm/entry-common.h>
>>>   
>>> @@ -237,6 +238,11 @@ static __always_inline void exit_to_user_mode_prepare(struct pt_regs *regs)
>>>   	/* Flush pending rcuog wakeup before the last need_resched() check */
>>>   	tick_nohz_user_enter_prepare();
>>>   
>>> +	/* Synchronize the in-band log before the work loop so that any deferred
>>> +	 * IRQs are played before we check for pending work. */
>>> +	if (running_inband())
>>> +		sync_inband_irqs();
>>> +
>>
>> I think there is more behind it. Seems your architecture (most likely
>> risc-v) is missing one corner case in the low level IRQ entry code:
>>
>> Potential stage demotion of the preempted task which originally entered
>> on the oob stage, then left it for the in-band stage as a result of IRQ
>> handling. This should happen in handle_irq_pipeline_finish() - if required.
>>
>> So on IRQ entry you are OOB, after the call to
>> handle_irq_pipeline_finish() you might be inband.
>>
>> In that case you most likely want to call irqentry_exit_to_user_mode()
>> with the inband stage stalled:
>>
>> Taken from arm64/x86:
>> if (unlikely(running_oob() || irqs_disabled())) {
>> 	...
>> 	handle_irq_pipelined_finish(prevd, regs);
>> 	if (running_inband() && user_mode(regs)) {
>> 		stall_inband_nocheck();
>> 		irqentry_exit_to_user_mode(regs);
>> 	}
>> 	...
>> }
>>
>>>   again:
>>>   	ti_work = read_thread_flags();
>>>   	if (unlikely(ti_work & EXIT_TO_USER_MODE_WORK))
>>
>> That said, we should double check that this corner case is handled right
>> on arm. If arm suffers from the same problem, this corner case is
>> missing in handle_irq_pipelined().
> 
> ARM is doing the right thing on assembly level already by calling into
> do_work_pending() on the way back.

You are right. This seems to be a leftover of a prior oob irq handling. 
Thanks for pointing me in the right direction.

A first test draft

'''
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 51e1407ee066..96d79cc3d3d4 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -527,6 +527,16 @@ DEFINE_PER_CPU(int, irq_nesting);

  asmlinkage void noinstr do_irq(struct pt_regs *regs)
  {
+       // OOB fast path
+       if (unlikely(running_oob() || irqs_disabled())) {
+               handle_irq_pipelined(regs);
+               if (running_inband() && user_mode(regs)) {
+                       stall_inband_nocheck();
+                       irqentry_exit_to_user_mode(regs);
+               }
+               return;
+       }
+
         irqentry_state_t state = irqentry_enter(regs);

  #ifdef CONFIG_IRQ_STACKS
'''

fixes it without touching the generic irq pipelining.

Seems like I can solve this issue in the riscv specific IRQ path.

Best,
Tobias

>>
>> I hope all of this makes sense at the end. It's late...
>>
>> Best regards,
>> Florian
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.