Re: [PATCH RFC Dovetail 7.1] arm64: irq_pipeline: 7.1 specific adjustments

Philippe Gerum <[email protected]> Tue, 16 Jun 2026 08:50:36 +0200
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
Florian Bezdeka <[email protected]> writes:

> On Thu, 2026-06-11 at 15:40 +0200, Florian Bezdeka wrote:
>> Signed-off-by: Florian Bezdeka <[email protected]>
>> ---
>> 
>> Hi Philippe,
>> 
>> final tests are still running but the local tests are looking good.
>> The EVL testsuite triggered a WARN_ONCE() - see process.c below - which
>> caused the systems to hang.
>> 
>> Turned out the arm64 entry code got some refactorings that needed some
>> adjustments - pipline wise.
>> 
>> I would fold the following into 7.1, so could you please double check?
>> Thanks!
>> 
>>  arch/arm64/kernel/entry-common.c | 35 ++++++++++++++++++++++++++++----
>>  arch/arm64/kernel/process.c      |  6 +++---
>>  include/linux/irq-entry-common.h |  3 +++
>>  3 files changed, 37 insertions(+), 7 deletions(-)
>> 
>> diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
>> index b0e141266349..97f6d6904a5d 100644
>> --- a/arch/arm64/kernel/entry-common.c
>> +++ b/arch/arm64/kernel/entry-common.c
>> @@ -166,7 +166,16 @@ static noinstr irqentry_state_t arm64_enter_from_kernel_mode(struct pt_regs *reg
>>  static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
>>  					      irqentry_state_t state)
>>  {
>> -	local_irq_disable();
>> +	if (irqs_pipelined()) {
>> +		hard_local_irq_disable();
>> +		if (running_inband()) {
>> +			stall_inband_nocheck();
>> +			trace_hardirqs_off();
>> +		}
>> +	} else {
>> +		local_irq_disable();
>> +	}
>> +
>>  	irqentry_exit_to_kernel_mode_preempt(regs, state);
>>  	local_daif_mask();
>>  	mte_check_tfsr_exit();
>> @@ -175,7 +184,14 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
>>  
>>  static __always_inline void arm64_syscall_enter_from_user_mode(struct pt_regs *regs)
>>  {
>> -	enter_from_user_mode(regs);
>> +	if (running_inband()) {
>> +		WARN_ON_ONCE(irq_pipeline_debug() && irqs_disabled());
>> +		stall_inband_nocheck();
>> +		enter_from_user_mode(regs);
>> +		trace_hardirqs_on();
>> +		unstall_inband_nocheck();
>> +	}
>
> Should enter_from_user_mode() be called while the inband stage is
> stalled, or after unstalling?

It should be stalled on entry, to reflect the hardware state.

> The exit part is called while being
> stalled, so I kept that in sync, but I'm not 100% sure.
>

That's correct.

>> +
>>  	mte_disable_tco_entry(current);
>>  	sme_enter_from_user_mode();
>>  }
>> @@ -191,6 +207,7 @@ static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
>>  		WARN_ON_ONCE(irq_pipeline_debug() && irqs_disabled());
>>  		stall_inband_nocheck();
>>  		enter_from_user_mode(regs);
>> +		rseq_note_user_irq_entry();
>>  		trace_hardirqs_on();
>>  		unstall_inband_nocheck();
>>  	}
>> @@ -226,8 +243,18 @@ static __always_inline void arm64_syscall_exit_to_user_mode(struct pt_regs *regs
>>   */
>>  static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
>>  {
>> -	local_irq_disable();
>> -	irqentry_exit_to_user_mode_prepare(regs);
>> +	if (irqs_pipelined()) {
>> +		hard_local_irq_disable();
>> +		if (running_inband()) {
>> +			stall_inband_nocheck();
>> +			trace_hardirqs_off();
>> +			irqentry_exit_to_user_mode_prepare(regs);
>> +		}
>> +	} else {
>> +		local_irq_disable();
>> +		irqentry_exit_to_user_mode_prepare(regs);
>> +	}
>> +
>>  	local_daif_mask();
>>  	sme_exit_to_user_mode();
>>  	mte_check_tfsr_exit();
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index b1eac5fd75c1..0047d0bdedd9 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -748,8 +748,8 @@ static inline void debug_switch_state(void)
>>  		unsigned long pmr_expected = GIC_PRIO_IRQOFF;
>>  		unsigned long pmr_actual = read_sysreg_s(SYS_ICC_PMR_EL1);
>>  
>> -		WARN_ONCE(daif_actual != daif_expected ||
>> -			  pmr_actual != pmr_expected,
>> +		WARN_ONCE(running_inband() && (daif_actual != daif_expected ||
>> +			  pmr_actual != pmr_expected),
>>  			  "Unexpected DAIF + PMR: 0x%lx + 0x%lx (expected 0x%lx + 0x%lx)\n",
>>  			  daif_actual, pmr_actual,
>>  			  daif_expected, pmr_expected);
>> @@ -757,7 +757,7 @@ static inline void debug_switch_state(void)
>>  		unsigned long daif_expected = DAIF_PROCCTX_NOIRQ;
>>  		unsigned long daif_actual = read_sysreg(daif);
>>  
>> -		WARN_ONCE(daif_actual != daif_expected,
>> +		WARN_ONCE(running_inband() && daif_actual != daif_expected,
>>  			  "Unexpected DAIF value: 0x%lx (expected 0x%lx)\n",
>>  			  daif_actual, daif_expected);
>
> That was the warning triggered by CI, that stalled the system. Is that
> expected? I'm quite sure that in the oob case the DAIF flags are
> different, but is WARN_ONCE() expected to completely halt the system?
>

WARN_ONCE() triggers a trap in the middle of the context switch
implementation, cobalt/evl are going to switch the caller to the in-band
stage in handle_oob_trap_entry(), which would cause __switch_to() calls
to pile up. So yes, this crash is definitely expected.

>
>>  	}
>> diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h
>> index 58c28b2a8f2b..61a8e129e71f 100644
>> --- a/include/linux/irq-entry-common.h
>> +++ b/include/linux/irq-entry-common.h
>> @@ -600,6 +600,9 @@ irqentry_exit_to_kernel_mode_after_preempt(struct pt_regs *regs, irqentry_state_
>>  {
>>  	bool synchronized;
>>  
>> +	if (running_oob())
>> +		return;
>> +
>
> The enter/exit code moved one layer down, from irqentry_enter() to
> irqentry_enter_from_kernel_mode(), so this code needs the oob check as
> well.

Ok.

>
>>  	instrumentation_begin();
>>  	synchronized = irqentry_syncstage(state);
>>  
>> -- 
>> 2.54.0

-- 
Philippe.