From nobody Wed Sep 30 16:20:07 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D91533E556D for ; Thu, 6 Aug 2026 21:24:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786051472; cv=none; b=aNYidaaZwC2MdfypZJuO1wfbHZyVcl/7BA7lAiL5DngeOBKcfOPG9OEHyqGBYVjedLcr7IejAYAnGS+TOjByAfNngSKF+kfUTjBbLH9w3YrDu4B28NPPUhJtf7IRhmENGA8sg8nF/MuXgkY89iaq88dzxNoPDoltkI8EsXwZRpQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786051472; c=relaxed/simple; bh=V1VZPZy4MFrrpbKLy9PGgfVNrxH+1o3heyGcvNelAJ8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=ZSEIK1dYqhy/Cqu2GpUygF6EEsbQv8s85s5U1ZRgA0JLEu180iOXU9Qp44LU8Pa9NV0zRK5Y+hxfkAL7ky3sLu1LwNxbjNoAQgGk5rMNWu3sTBSC6jUDamsI8n/NLgBenZ/b94DAY4N4QTN6QNI01QVL9Uyssk8oo5m2WEnKRI8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FPI0uSKL; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FPI0uSKL" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 85F1C1F000E9; Thu, 6 Aug 2026 21:24:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786051470; bh=YPVbFpPk+Vi0AmZcunxEr4n8OBahnerbMMu/2B4EUAE=; h=From:Date:Subject:To:Cc; b=FPI0uSKL+zmQvniTNzX542qdvkG7ucYhPqJfb3LOpXWotq3Rby6IOAfDbX0ag6YRR AkTLDwGkEQ3cvoUR0wYrrF7ozK4dlJrn00JcKQPPhnKs59shXHxWlMyEb5Bw4NZHxA mzWOXOAiNsuObZElDMQs6Yza0TSNS4artTJH8SvP+UqYzisU4XeCa6W94mL9l7e3+U vYIq4c5vmrDT5c80FxKf0ox42Pd6fGu7WCknaYkeF+9cNj0LSgsg4ZSo+LJycK0fpB 4Zcp6pLtfK+9PBrLerHSY6JgbSrVdZv9ooKplqykfZZHvkk5zMVX8FIKE6zWNWxuD/ ejUrF13WSNJkQ== From: Linus Walleij Date: Thu, 06 Aug 2026 23:24:17 +0200 Subject: [PATCH v2] ARM: entry: Convert IRQ handling to generic IRQ entry Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260806-arm-generic-irq-entry-v7-1-v2-1-3dd7747b2567@kernel.org> X-B4-Tracking: v=1; b=H4sIAAAAAAAC/32Nyw6CMBBFf4V07RhatIgr/8OwgGGA+ig6xUZC+ HdbjFuXJ7n3nFk4YkNOHJNZMHnjzGADqE0isK9sR2CawEKlSqdaKaj4Dh3Z8EIw/ASyI0/gc5D Qthplo7HA3UEEwYOpNe9Vfi6/7F71hXCMxrjojRsHnta6l3H3C2X/Ql7Gmtpjlad5QVl9uhJbu m0H7kS5LMsHbQSKy9cAAAA= X-Change-ID: 20260622-arm-generic-irq-entry-v7-1-ff6c1d6c9c48 To: "Paul E. McKenney" , Jinjie Ruan , Arnd Bergmann , Russell King , Oleg Nesterov , Thomas Gleixner Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Linus Walleij X-Mailer: b4 0.16.0 Bring ARM to the same level of generic-ness as ARM64 by enabling GENERIC_IRQ_ENTRY. Route ARM IRQ entry through C wrappers that use the generic IRQ entry helpers. Conversely put the FIQ entry under the generic NMI-style entry helpers. Select GENERIC_IRQ_ENTRY, add the ARM entry prototypes, and provide regs_irqs_disabled() for the generic IRQ entry return path. The kernel-mode IRQ return path relies on the generic IRQ entry preemption handling, matching the arm64 structure and avoiding the old assembly reschedule check. The reschedule check is now done in raw_irqentry_exit_cond_resched() in kernel/entry/common.c. User-mode IRQs __irq_usr is neither calling ct_user_exit/enter or asm_trace_hardirqs_off/on anymore. The corresponding calls happen on irqentry_enter/exit() call paths, e.g the irq-disabled C variants __ct_user_exit/enter() are called instead. As __irq_usr no longer returns by jumping to ret_to_user_from_irq, the corresponding code has been inlined, except the slow_work_pending part, which is now also handled by generic entry. ret_to_user_from_irq is only called from v7m so it has been renamed accordingly. arch_do_signal_or_restart() is required, but we only need a very small stub, since we keep the existing syscall restart code around in assembly. Tested on with multi_v7_defconfig and qemu-system-arm vexpress-a15 using vexpress-v2p-ca15-tc1.dtb and some different loads. A note on V7M: the reason we cannot switch the V7M entry over to generic entry is that it raises a "pendable service call" ("pendv") exception to process deferred work at the end of the interrupt handler. This is done so higher priority work can come in. This has no corresponding structure in other ARM cores. This is a reduced patch based on the earlier generic entry series avoiding all the syscall handling changes. Link: https://lore.kernel.org/linux-arm-kernel/20250420-arm-generic-entry-v= 6-0-95f1fcdfeeb2@linaro.org/ Signed-off-by: Linus Walleij --- Changes in v2: - Rebase on v7.2-rc1 - Select GENERIC_IRQ_ENTRY also for V7M even if this platform is not using it, in order to properly link the image. - Link to v1: https://patch.msgid.link/20260623-arm-generic-irq-entry-v7-1-= v1-1-f25ca7079e3b@kernel.org --- arch/arm/Kconfig | 1 + arch/arm/include/asm/entry-common.h | 7 ++++ arch/arm/include/asm/entry.h | 10 +++++ arch/arm/include/asm/ptrace.h | 7 +++- arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/entry-armv.S | 75 ++++++++-------------------------= ---- arch/arm/kernel/entry-common.S | 5 ++- arch/arm/kernel/entry-header.S | 7 +++- arch/arm/kernel/entry-v7m.S | 2 +- arch/arm/kernel/entry.c | 51 +++++++++++++++++++++++++ arch/arm/kernel/irq.c | 6 +++ arch/arm/kernel/irq.h | 2 + arch/arm/kernel/signal.c | 6 +++ 13 files changed, 116 insertions(+), 65 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9187240a02db..48daf8a8e5a7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -71,6 +71,7 @@ config ARM select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_DEVICES select GENERIC_EARLY_IOREMAP + select GENERIC_IRQ_ENTRY select GENERIC_IDLE_POLL_SETUP select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_PROBE diff --git a/arch/arm/include/asm/entry-common.h b/arch/arm/include/asm/ent= ry-common.h new file mode 100644 index 000000000000..c017df5f39d5 --- /dev/null +++ b/arch/arm/include/asm/entry-common.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_ARM_ENTRY_COMMON_H +#define __ASM_ARM_ENTRY_COMMON_H + +#include + +#endif diff --git a/arch/arm/include/asm/entry.h b/arch/arm/include/asm/entry.h new file mode 100644 index 000000000000..864c9b3abbf1 --- /dev/null +++ b/arch/arm/include/asm/entry.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_ENTRY_H__ +#define __ASM_ENTRY_H__ + +struct pt_regs; + +void arm_irq_handler(struct pt_regs *regs, int mode); +void arm_fiq_handler(struct pt_regs *regs); + +#endif /* __ASM_ENTRY_H__ */ diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 6eb311fb2da0..88ddb9371a02 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -46,8 +46,13 @@ struct svc_pt_regs { #define processor_mode(regs) \ ((regs)->ARM_cpsr & MODE_MASK) =20 +static inline bool regs_irqs_disabled(const struct pt_regs *regs) +{ + return regs->ARM_cpsr & PSR_I_BIT; +} + #define interrupts_enabled(regs) \ - (!((regs)->ARM_cpsr & PSR_I_BIT)) + (!regs_irqs_disabled(regs)) =20 #define fast_interrupts_enabled(regs) \ (!((regs)->ARM_cpsr & PSR_F_BIT)) diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index b36cf0cfd4a7..3b8a62f6f54d 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -17,7 +17,7 @@ CFLAGS_REMOVE_return_address.o =3D -pg =20 # Object file lists. =20 -obj-y :=3D elf.o entry-common.o irq.o opcodes.o \ +obj-y :=3D elf.o entry.o entry-common.o irq.o opcodes.o \ process.o ptrace.o reboot.o io.o \ setup.o signal.o sigreturn_codes.o \ stacktrace.o sys_arm.o time.o traps.o diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index a3d050ce9b79..8ac7f1512ee8 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -36,35 +36,6 @@ #define RELOC_TEXT_NONE #endif =20 -/* - * Interrupt handling. - */ - .macro irq_handler, from_user:req - mov r1, sp - ldr_this_cpu r2, irq_stack_ptr, r2, r3 - .if \from_user =3D=3D 0 - @ - @ If we took the interrupt while running in the kernel, we may already - @ be using the IRQ stack, so revert to the original value in that case. - @ - subs r3, r2, r1 @ SP above bottom of IRQ stack? - rsbscs r3, r3, #THREAD_SIZE @ ... and below the top? -#ifdef CONFIG_VMAP_STACK - ldr_va r3, high_memory, cc @ End of the linear region - cmpcc r3, r1 @ Stack pointer was below it? -#endif - bcc 0f @ If not, switch to the IRQ stack - mov r0, r1 - bl generic_handle_arch_irq - b 1f -0: - .endif - - mov_l r0, generic_handle_arch_irq - bl call_with_stack -1: - .endm - .macro pabt_helper @ PABORT handler takes pt_regs in r2, fault address in r4 and psr in r5 #ifdef MULTI_PABORT @@ -224,34 +195,17 @@ ENDPROC(__dabt_svc) =20 .align 5 __irq_svc: - svc_entry - irq_handler from_user=3D0 - -#ifdef CONFIG_PREEMPTION - ldr r8, [tsk, #TI_PREEMPT] @ get preempt count - ldr r0, [tsk, #TI_FLAGS] @ get flags - teq r8, #0 @ if preempt count !=3D 0 - movne r0, #0 @ force flags to 0 - tst r0, #_TIF_NEED_RESCHED - blne svc_preempt -#endif + svc_entry trace=3D0 + mov r0, sp @ regs + mov r1, #0 @ from kernel mode + bl arm_irq_handler =20 - svc_exit r5, irq =3D 1 @ return from exception + svc_exit r5, irqentry =3D 1 @ return from exception UNWIND(.fnend ) ENDPROC(__irq_svc) =20 .ltorg =20 -#ifdef CONFIG_PREEMPTION -svc_preempt: - mov r8, lr -1: bl preempt_schedule_irq @ irq en/disable is done inside - ldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGS - tst r0, #_TIF_NEED_RESCHED - reteq r8 @ go again - b 1b -#endif - __und_fault: @ Correct the PC such that it is pointing at the instruction @ which caused the fault. If the faulting instruction was ARM @@ -302,7 +256,7 @@ ENDPROC(__pabt_svc) __fiq_svc: svc_entry trace=3D0 mov r0, sp @ struct pt_regs *regs - bl handle_fiq_as_nmi + bl arm_fiq_handler svc_exit_via_fiq UNWIND(.fnend ) ENDPROC(__fiq_svc) @@ -331,7 +285,7 @@ __fiq_abt: stmfd sp!, {r1 - r2} =20 add r0, sp, #8 @ struct pt_regs *regs - bl handle_fiq_as_nmi + bl arm_fiq_handler =20 ldmfd sp!, {r1 - r2} ARM( msr cpsr_c, #ABT_MODE | PSR_I_BIT | PSR_F_BIT ) @@ -438,12 +392,15 @@ ENDPROC(__dabt_usr) =20 .align 5 __irq_usr: - usr_entry + usr_entry trace=3D0 kuser_cmpxchg_check - irq_handler from_user=3D1 - get_thread_info tsk - mov why, #0 - b ret_to_user_from_irq + mov r0, sp @ regs + mov r1, #1 @ from user mode + bl arm_irq_handler +#ifdef CONFIG_KSTACK_ERASE + bl stackleak_erase_on_task_stack +#endif + restore_user_regs fast =3D 0, offset =3D 0 UNWIND(.fnend ) ENDPROC(__irq_usr) =20 @@ -498,7 +455,7 @@ __fiq_usr: usr_entry trace=3D0 kuser_cmpxchg_check mov r0, sp @ struct pt_regs *regs - bl handle_fiq_as_nmi + bl arm_fiq_handler get_thread_info tsk restore_user_regs fast =3D 0, offset =3D 0 UNWIND(.fnend ) diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 88336a1292bb..d5fdb234c1d4 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -110,7 +110,8 @@ ret_slow_syscall: bl do_rseq_syscall #endif disable_irq_notrace @ disable interrupts -ENTRY(ret_to_user_from_irq) +ENTRY(v7m_ret_to_user_from_irq) + /* Only the v7m jumps directly to v7m_ret_to_user_from_irq */ ldr r1, [tsk, #TI_FLAGS] movs r1, r1, lsl #16 bne slow_work_pending @@ -123,7 +124,7 @@ no_work_pending: bl stackleak_erase_on_task_stack #endif restore_user_regs fast =3D 0, offset =3D 0 -ENDPROC(ret_to_user_from_irq) +ENDPROC(v7m_ret_to_user_from_irq) ENDPROC(ret_to_user) =20 /* diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 99411fa91350..e83f2fb8a592 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -199,7 +199,11 @@ .endm =20 =20 - .macro svc_exit, rpsr, irq =3D 0 + .macro svc_exit, rpsr, irq =3D 0, irqentry =3D 0 + .if \irqentry !=3D 0 + @ Generic IRQ entry already handled tracing and lockdep state. + disable_irq_notrace + .else .if \irq !=3D 0 @ IRQs already off #ifdef CONFIG_TRACE_IRQFLAGS @@ -216,6 +220,7 @@ tst \rpsr, #PSR_I_BIT blne trace_hardirqs_off #endif + .endif .endif uaccess_exit tsk, r0, r1 =20 diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S index 52bacf07ba16..49a3a34e2913 100644 --- a/arch/arm/kernel/entry-v7m.S +++ b/arch/arm/kernel/entry-v7m.S @@ -94,7 +94,7 @@ __pendsv_entry: @ execute the pending work, including reschedule get_thread_info tsk mov why, #0 - b ret_to_user_from_irq + b v7m_ret_to_user_from_irq ENDPROC(__pendsv_entry) =20 /* diff --git a/arch/arm/kernel/entry.c b/arch/arm/kernel/entry.c new file mode 100644 index 000000000000..1142e418d161 --- /dev/null +++ b/arch/arm/kernel/entry.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include +#include +#include + +#include "irq.h" + +static void noinstr handle_arm_irq(void *data) +{ + struct pt_regs *regs =3D data; + struct pt_regs *old_regs; + + irq_enter_rcu(); + old_regs =3D set_irq_regs(regs); + + handle_arch_irq(regs); + + set_irq_regs(old_regs); + irq_exit_rcu(); +} + +noinstr void arm_irq_handler(struct pt_regs *regs, int mode) +{ + irqentry_state_t state =3D irqentry_enter(regs); + + /* + * mode =3D=3D 1 means we came from userspace, and then we + * should just immediately switch to the irq stack. + * Then we check of we are on the thread stack. If we are + * not, then by definition we are already using the irq stack. + */ + if (mode =3D=3D 1 || on_thread_stack()) + call_on_irq_stack(handle_arm_irq, regs); + else + handle_arm_irq(regs); + + irqentry_exit(regs, state); +} + +noinstr void arm_fiq_handler(struct pt_regs *regs) +{ + irqentry_state_t state =3D irqentry_nmi_enter(regs); + + handle_fiq_as_nmi(regs); + + irqentry_nmi_exit(regs, state); +} diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index e1993e28a9ec..f99d6b24d8ff 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -43,6 +43,7 @@ #include #include =20 +#include "irq.h" #include "reboot.h" =20 unsigned long irq_err_count; @@ -71,6 +72,11 @@ static void __init init_irq_stacks(void) } } =20 +void call_on_irq_stack(void (*fn)(void *), void *arg) +{ + call_with_stack(fn, arg, __this_cpu_read(irq_stack_ptr)); +} + #ifdef CONFIG_SOFTIRQ_ON_OWN_STACK static void ____do_softirq(void *arg) { diff --git a/arch/arm/kernel/irq.h b/arch/arm/kernel/irq.h new file mode 100644 index 000000000000..80dd5bfe6403 --- /dev/null +++ b/arch/arm/kernel/irq.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +void call_on_irq_stack(void (*fn)(void *), void *arg); diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7be9188d83d9..9084c04c07f7 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -12,6 +12,7 @@ #include #include #include +#include =20 #include #include @@ -599,6 +600,11 @@ static int do_signal(struct pt_regs *regs, int syscall) return 0; } =20 +void arch_do_signal_or_restart(struct pt_regs *regs) +{ + do_signal(regs, 0); +} + asmlinkage int do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int sysca= ll) { --- base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482 change-id: 20260622-arm-generic-irq-entry-v7-1-ff6c1d6c9c48 Best regards, -- =20 Linus Walleij