From 123b4fc0f8576ac29b965cfc362630f51fa0fe7e Mon Sep 17 00:00:00 2001 From: "Jose Fernandez (Anthropic)" Date: Tue, 9 Jun 2026 05:19:26 +0000 Subject: [PATCH 1/6] arm64: ftrace: prepare ftrace_modify_call() for use without CALL_OPS ftrace_modify_call() is guarded by CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS and calls ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec)) directly, which only exists when CALL_OPS is enabled. Generic ftrace also needs ftrace_modify_call() when CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS is enabled, to retarget a callsite between two non-FTRACE_ADDR destinations, as happens when a direct trampoline is modified. The next patch allows DIRECT_CALLS without CALL_OPS, so widen the guard to cover both configurations and switch the body to the ftrace_rec_update_ops() wrapper, which already has a stub for the !CALL_OPS case. ftrace_make_call() already uses the same wrapper today. No functional change: with CALL_OPS enabled, ftrace_rec_update_ops() expands to the exact call this replaces. Assisted-by: Claude:unspecified Signed-off-by: Jose Fernandez (Anthropic) Acked-by: Xu Kuohai Tested-by: Nathan Chancellor Tested-by: Clayton Craft Reviewed-by: Puranjay Mohan Signed-off-by: Will Deacon --- arch/arm64/kernel/ftrace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index 5a1554a44162..e1a3c0b3a051 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -409,7 +409,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return ftrace_modify_code(pc, old, new, true); } -#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS +#if defined(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS) || \ + defined(CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS) int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr) { @@ -417,7 +418,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, u32 old, new; int ret; - ret = ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec)); + ret = ftrace_rec_update_ops(rec); if (ret) return ret; From 9315e22b0c0a5be708798c03dc8f27549667e475 Mon Sep 17 00:00:00 2001 From: "Jose Fernandez (Anthropic)" Date: Tue, 9 Jun 2026 05:19:27 +0000 Subject: [PATCH 2/6] arm64: ftrace: allow DIRECT_CALLS without CALL_OPS Drop the CALL_OPS requirement from the HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select. Configurations that keep CALL_OPS (!CFI clang builds, and GCC builds without CC_OPTIMIZE_FOR_SIZE) are unchanged. CALL_OPS-less configurations take the ftrace_caller ops-dispatch path for out-of-range direct calls, trading the per-callsite fast path for working BPF trampolines; in-range attachments still branch directly with no overhead. GCC -Os builds also gain DIRECT_CALLS as a side effect. That is intended: s390 and loongarch already ship DIRECT_CALLS without any per-callsite fast path. Assisted-by: Claude:unspecified Signed-off-by: Jose Fernandez (Anthropic) Acked-by: Xu Kuohai Tested-by: Nathan Chancellor Tested-by: Clayton Craft Reviewed-by: Puranjay Mohan Signed-off-by: Will Deacon --- arch/arm64/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b3afe0688919..0de419ed780f 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -187,7 +187,7 @@ config ARM64 if (GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS || \ CLANG_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS) select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS \ - if DYNAMIC_FTRACE_WITH_ARGS && DYNAMIC_FTRACE_WITH_CALL_OPS + if DYNAMIC_FTRACE_WITH_ARGS select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS \ if (DYNAMIC_FTRACE_WITH_ARGS && !CFI && \ (CC_IS_CLANG || !CC_OPTIMIZE_FOR_SIZE)) From 25ef34b541eb216c09cb304778915fec845ef7ec Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 23 Jul 2026 14:03:04 +0100 Subject: [PATCH 3/6] arm64: futex: Consolidate 'old == new' check in __lsui_cmpxchg32() The LSUI futex implementation relies on a cmpxchg() loop to implement FUTEX_OP_XOR, as the architecture doesn't provide unprivileged *EOR atomics. Since the unprivileged 'CAST' instructions used to implement the cmpxchg() can only operate on 64-bit memory locations, the __lsui_cmpxchg32() helper function performs a song and dance to marshall the 32-bit futex value into the correct part of a 64-bit register and fill the remaining bytes with the neighbouring data. A consequence of this structure is that the 'CAST' failure/success condition ends up being split into two separate 32-bit checks across __lsui_cmpxchg32() and its caller. This is a little fiddly to read and introduces some additional local variables which can be avoided if the check is done in one place. Tweak __lsui_cmpxchg32() so that it performs the full 64-bit check on the value returned from the 'CAST' instruction and returns success to its caller only in the case that the cmpxchg() operation has succeeded. With that in place, simplify the outer loop in __lsui_futex_atomic_eor() to pass 'oldval' by reference and return unless the cmpxchg() operation returns -EAGAIN. __lsui_futex_cmpxchg() then swallows the -EAGAIN if the futex word has changed. Cc: Catalin Marinas Cc: Yeoreum Yun Reviewed-by: Yeoreum Yun Signed-off-by: Will Deacon --- arch/arm64/include/asm/futex.h | 61 ++++++++++++---------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h index d1d2ff9d323a..79c6d86c38a9 100644 --- a/arch/arm64/include/asm/futex.h +++ b/arch/arm64/include/asm/futex.h @@ -151,42 +151,31 @@ __lsui_cmpxchg64(u64 __user *uaddr, u64 *oldval, u64 newval) } static __always_inline int -__lsui_cmpxchg32(u32 __user *uaddr, u32 oldval, u32 newval, u32 *oval) +__lsui_cmpxchg32(u32 __user *uaddr, u32 *oldval, u32 newval) { u64 __user *uaddr64; bool futex_pos, other_pos; - u32 other, orig_other; union { u32 futex[2]; u64 raw; - } oval64, orig64, nval64; + } orig64, oval64, nval64; uaddr64 = (u64 __user *)PTR_ALIGN_DOWN(uaddr, sizeof(u64)); futex_pos = !IS_ALIGNED((unsigned long)uaddr, sizeof(u64)); other_pos = !futex_pos; - oval64.futex[futex_pos] = oldval; - if (get_user(oval64.futex[other_pos], (u32 __user *)uaddr64 + other_pos)) + orig64.futex[futex_pos] = *oldval; + if (get_user(orig64.futex[other_pos], (u32 __user *)uaddr64 + other_pos)) return -EFAULT; - orig64.raw = oval64.raw; - + nval64 = oval64 = orig64; nval64.futex[futex_pos] = newval; - nval64.futex[other_pos] = oval64.futex[other_pos]; if (__lsui_cmpxchg64(uaddr64, &oval64.raw, nval64.raw)) return -EFAULT; - oldval = oval64.futex[futex_pos]; - other = oval64.futex[other_pos]; - orig_other = orig64.futex[other_pos]; - - if (other != orig_other) - return -EAGAIN; - - *oval = oldval; - - return 0; + *oldval = oval64.futex[futex_pos]; + return oval64.raw == orig64.raw ? 0 : -EAGAIN; } static __always_inline int @@ -202,7 +191,7 @@ __lsui_futex_atomic_and(int oparg, u32 __user *uaddr, int *oval) static __always_inline int __lsui_futex_atomic_eor(int oparg, u32 __user *uaddr, int *oval) { - u32 oldval, newval, val; + u32 oldval, newval; int ret, i; if (get_user(oldval, uaddr)) @@ -214,33 +203,27 @@ __lsui_futex_atomic_eor(int oparg, u32 __user *uaddr, int *oval) for (i = 0; i < FUTEX_MAX_LOOPS; i++) { newval = oldval ^ oparg; - ret = __lsui_cmpxchg32(uaddr, oldval, newval, &val); - switch (ret) { - case -EFAULT: - return ret; - case -EAGAIN: - continue; - } - - if (val == oldval) { - *oval = val; - return 0; - } - - oldval = val; + ret = __lsui_cmpxchg32(uaddr, &oldval, newval); + if (ret != -EAGAIN) + break; } - return -EAGAIN; + *oval = oldval; + return ret; } static __always_inline int __lsui_futex_cmpxchg(u32 __user *uaddr, u32 oldval, u32 newval, u32 *oval) { - /* - * Callers of futex_atomic_cmpxchg_inatomic() already retry on - * -EAGAIN, no need for another loop of max retries. - */ - return __lsui_cmpxchg32(uaddr, oldval, newval, oval); + u32 curval = oldval; + int ret; + + ret = __lsui_cmpxchg32(uaddr, &curval, newval); + if (ret == -EAGAIN && curval != oldval) + ret = 0; + + *oval = curval; + return ret; } #endif /* CONFIG_ARM64_LSUI */ From 4c9c81a0860415284e9d260f998fbd755d3a7469 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Fri, 31 Jul 2026 13:26:46 +0100 Subject: [PATCH 4/6] arm64: smp: Fix IPI teardown for GICv5 flow Sashiko reported that during CPU offlining, __cpu_disable() is executed by the stopper thread via take_cpu_down() with local interrupts disabled. __cpu_disable() calls ipi_teardown(), which invokes ipi_lpi_disable(). For the GICv5 flow, this eventually calls the sleepable disable_irq(). This can be reproduced easily with CONFIG_DEBUG_ATOMIC_SLEEP=y by offlining a CPU: BUG: sleeping function called from invalid context at kernel/irq/manage.c:702 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 20, name: migration/1 preempt_count: 1, expected: 0 no locks held by migration/1/20. irq event stamp: 186 hardirqs last enabled at (185): [] _raw_spin_unlock_irq+0x38/0x68 hardirqs last disabled at (186): [] multi_cpu_stop+0xc8/0x190 softirqs last enabled at (80): [] handle_softirqs+0x410/0x468 softirqs last disabled at (75): [] __do_softirq+0x1c/0x28 Fix this by using disable_irq_nosync() instead, which is safe in this atomic context. Fixes: ba1004f861d1 ("arm64: smp: Support non-SGIs for IPIs") Signed-off-by: Vladimir Murzin Signed-off-by: Will Deacon --- arch/arm64/kernel/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index cdcdd160e5b6..3ab90aa24efb 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -1086,7 +1086,7 @@ static void ipi_teardown(int cpu) disable_percpu_irq(ipi_irq_base + i); } } else { - disable_irq(irq_desc_get_irq(get_ipi_desc(cpu, i))); + disable_irq_nosync(irq_desc_get_irq(get_ipi_desc(cpu, i))); } } } From 71dae04644e19bddda9e06d8e32849e0618f9cc9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 3 Aug 2026 17:42:30 +0100 Subject: [PATCH 5/6] MAINTAINERS: arm64: Add Mark Rutland as an official Reviewer Mark has been part of the arm64 furniture for as long as the code has existed in mainline and is consistently one of the most active and knowledgeable reviewers that we have. Make it official. Acked-by: Ard Biesheuvel Acked-by: Mark Rutland Acked-by: Catalin Marinas Acked-by: Marc Zyngier Signed-off-by: Will Deacon --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 806bd2d80d15..08328eb1064d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3913,6 +3913,7 @@ F: drivers/platform/arm64/ ARM64 PORT (AARCH64 ARCHITECTURE) M: Catalin Marinas M: Will Deacon +R: Mark Rutland L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git From 5eaec4cf41a8f5ac1a0c69a607cdc5035a797f73 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 7 Aug 2026 13:37:32 +0000 Subject: [PATCH 6/6] arm64: Disable KCSAN instrumentation in delay.o KCSAN relies on udelay() for injecting delays. To avoid recursively triggering a watchpoint, where KCSAN sets up watchpoint on an address that is accessed by udelay() in the same thread, disable instrumentation in arm64's delay implementation. Paul found a manifestation of this as follows: | BUG: KCSAN: data-race in __delay / set_need_resched_current | | read (marked) to 0xffff000005899b48 of 8 bytes by interrupt on cpu 8: | __delay+0xb0/0x378 | __udelay+0x4c/0x60 | kcsan_setup_watchpoint+0x3b4/0x820 | __tsan_unaligned_write4+0x228/0x26c | set_need_resched_current+0x138/0x1a8 | rcu_exp_handler+0x418/0x4a0 | __flush_smp_call_function_queue+0x36c/0x4a0 | generic_smp_call_function_single_interrupt+0x20/0x30 | ipi_handler+0xec/0x558 | handle_percpu_devid_irq+0x220/0x2a0 | generic_handle_domain_irq+0x84/0xb4 | gic_handle_irq+0x64/0x144 | call_on_irq_stack+0x30/0x48 | do_interrupt_handler+0x80/0xb8 | el1_interrupt+0x3c/0x60 | el1h_64_irq_handler+0x18/0x24 | el1h_64_irq+0x6c/0x70 | smp_call_function_single+0x18c/0x25c | sync_rcu_exp_select_node_cpus+0x534/0x8bc | rcu_exp_sel_wait_wake+0x358/0xef4 | wait_rcu_exp_gp+0x30/0x44 | kthread_worker_fn+0x1b4/0x5dc | kthread+0x1d8/0x204 | ret_from_fork+0x10/0x20 | | write to 0xffff000005899b4c of 4 bytes by interrupt on cpu 8: | set_need_resched_current+0x138/0x1a8 | [...] This matches what is already done in arch/x86/lib/Makefile. Reported-by: "Paul E. McKenney" Fixes: dd03762ab608 ("arm64: Enable KCSAN") Signed-off-by: Marco Elver Signed-off-by: Will Deacon --- arch/arm64/lib/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index 448c917494f3..b33e1ca4a781 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -1,4 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 + +# KCSAN uses udelay for introducing watchpoint delay; avoid recursion. +KCSAN_SANITIZE_delay.o := n + lib-y := clear_user.o delay.o copy_from_user.o \ copy_to_user.o copy_page.o \ clear_page.o csum.o insn.o memchr.o memcpy.o \